This commit is contained in:
tracer 2022-04-11 16:05:12 +02:00
parent abaa95f71d
commit 6309faa898
37 changed files with 1263 additions and 7 deletions

4
README.md Normal file
View File

@ -0,0 +1,4 @@
TODO:
Make user work
make quotes work

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

BIN
assets/images/tracer_schmolle.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

View File

@ -5,5 +5,25 @@ require('bootstrap');
import '../styles/app.scss'; import '../styles/app.scss';
// CKEditor
//require '@'
import '../styles/ckeditor.css';
require('@fortawesome/fontawesome-free/css/all.min.css'); require('@fortawesome/fontawesome-free/css/all.min.css');
require('@fortawesome/fontawesome-free/js/all.js'); require('@fortawesome/fontawesome-free/js/all.js');
window.Dropzone = require('@symfony/ux-dropzone/dist/controller');
Dropzone.autodiscover = false;
/*
initializeDropzone();
function initializeDropzone()
{
let formElement = document.querySelector('js-dropzone');
}
*/

106
assets/styles/ckeditor.css Normal file
View File

@ -0,0 +1,106 @@
:root {
/* Overrides the border radius setting in the theme. */
--ck-border-radius: 4px;
/* Overrides the default font size in the theme. */
--ck-font-size-base: 14px;
/* Helper variables to avoid duplication in the colors. */
--ck-custom-background: hsl(270, 1%, 29%);
--ck-custom-foreground: hsl(255, 3%, 18%);
--ck-custom-border: hsl(300, 1%, 22%);
--ck-custom-white: hsl(0, 0%, 100%);
/* -- Overrides generic colors. ------------------------------------------------------------- */
--ck-color-base-foreground: var(--ck-custom-background);
--ck-color-focus-border: hsl(208, 90%, 62%);
--ck-color-text: hsl(0, 0%, 98%);
--ck-color-shadow-drop: hsla(0, 0%, 0%, 0.2);
--ck-color-shadow-inner: hsla(0, 0%, 0%, 0.1);
/* -- Overrides the default .ck-button class colors. ---------------------------------------- */
--ck-color-button-default-background: var(--ck-custom-background);
--ck-color-button-default-hover-background: hsl(270, 1%, 22%);
--ck-color-button-default-active-background: hsl(270, 2%, 20%);
--ck-color-button-default-active-shadow: hsl(270, 2%, 23%);
--ck-color-button-default-disabled-background: var(--ck-custom-background);
--ck-color-button-on-background: var(--ck-custom-foreground);
--ck-color-button-on-hover-background: hsl(255, 4%, 16%);
--ck-color-button-on-active-background: hsl(255, 4%, 14%);
--ck-color-button-on-active-shadow: hsl(240, 3%, 19%);
--ck-color-button-on-disabled-background: var(--ck-custom-foreground);
--ck-color-button-action-background: hsl(168, 76%, 42%);
--ck-color-button-action-hover-background: hsl(168, 76%, 38%);
--ck-color-button-action-active-background: hsl(168, 76%, 36%);
--ck-color-button-action-active-shadow: hsl(168, 75%, 34%);
--ck-color-button-action-disabled-background: hsl(168, 76%, 42%);
--ck-color-button-action-text: var(--ck-custom-white);
--ck-color-button-save: hsl(120, 100%, 46%);
--ck-color-button-cancel: hsl(15, 100%, 56%);
/* -- Overrides the default .ck-dropdown class colors. -------------------------------------- */
--ck-color-dropdown-panel-background: var(--ck-custom-background);
--ck-color-dropdown-panel-border: var(--ck-custom-foreground);
/* -- Overrides the default .ck-splitbutton class colors. ----------------------------------- */
--ck-color-split-button-hover-background: var(--ck-color-button-default-hover-background);
--ck-color-split-button-hover-border: var(--ck-custom-foreground);
/* -- Overrides the default .ck-input class colors. ----------------------------------------- */
--ck-color-input-background: var(--ck-custom-background);
--ck-color-input-border: hsl(257, 3%, 43%);
--ck-color-input-text: hsl(0, 0%, 98%);
--ck-color-input-disabled-background: hsl(255, 4%, 21%);
--ck-color-input-disabled-border: hsl(250, 3%, 38%);
--ck-color-input-disabled-text: hsl(0, 0%, 78%);
/* -- Overrides the default .ck-labeled-field-view class colors. ---------------------------- */
--ck-color-labeled-field-label-background: var(--ck-custom-background);
/* -- Overrides the default .ck-list class colors. ------------------------------------------ */
--ck-color-list-background: var(--ck-custom-background);
--ck-color-list-button-hover-background: var(--ck-color-base-foreground);
--ck-color-list-button-on-background: var(--ck-color-base-active);
--ck-color-list-button-on-background-focus: var(--ck-color-base-active-focus);
--ck-color-list-button-on-text: var(--ck-color-base-background);
/* -- Overrides the default .ck-balloon-panel class colors. --------------------------------- */
--ck-color-panel-background: var(--ck-custom-background);
--ck-color-panel-border: var(--ck-custom-border);
/* -- Overrides the default .ck-toolbar class colors. --------------------------------------- */
--ck-color-toolbar-background: var(--ck-custom-background);
--ck-color-toolbar-border: var(--ck-custom-border);
/* -- Overrides the default .ck-tooltip class colors. --------------------------------------- */
--ck-color-tooltip-background: hsl(252, 7%, 14%);
--ck-color-tooltip-text: hsl(0, 0%, 93%);
/* -- Overrides the default colors used by the ckeditor5-image package. --------------------- */
--ck-color-image-caption-background: hsl(0, 0%, 97%);
--ck-color-image-caption-text: hsl(0, 0%, 20%);
/* -- Overrides the default colors used by the ckeditor5-widget package. -------------------- */
--ck-color-widget-blurred-border: hsl(0, 0%, 87%);
--ck-color-widget-hover-border: hsl(43, 100%, 68%);
--ck-color-widget-editable-focus-background: var(--ck-custom-white);
/* -- Overrides the default colors used by the ckeditor5-link package. ---------------------- */
--ck-color-link-default: hsl(190, 100%, 75%);
}

11
bootstrap.js vendored Normal file
View File

@ -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);

View File

@ -0,0 +1,5 @@
when@dev:
debug:
# Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser.
# See the "server:dump" command to start a new server.
dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%"

View File

@ -0,0 +1,24 @@
framework:
messenger:
failure_transport: failed
transports:
# https://symfony.com/doc/current/messenger.html#transport-configuration
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
use_notify: true
check_delayed_interval: 60000
retry_strategy:
max_retries: 3
multiplier: 2
failed: 'doctrine://default?queue_name=failed'
# sync: 'sync://'
routing:
Symfony\Component\Mailer\Messenger\SendEmailMessage: async
Symfony\Component\Notifier\Message\ChatMessage: async
Symfony\Component\Notifier\Message\SmsMessage: async
# Route your messages to the transports
# 'App\Message\YourMessage': async

View File

@ -0,0 +1,61 @@
monolog:
channels:
- deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists
when@dev:
monolog:
handlers:
main:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
channels: ["!event"]
# uncomment to get logging in your browser
# you may have to allow bigger header sizes in your Web server configuration
#firephp:
# type: firephp
# level: info
#chromephp:
# type: chromephp
# level: info
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine", "!console"]
when@test:
monolog:
handlers:
main:
type: fingers_crossed
action_level: error
handler: nested
excluded_http_codes: [404, 405]
channels: ["!event"]
nested:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
when@prod:
monolog:
handlers:
main:
type: fingers_crossed
action_level: error
handler: nested
excluded_http_codes: [404, 405]
buffer_size: 50 # How many messages should be saved? Prevent memory leaks
nested:
type: stream
path: php://stderr
level: debug
formatter: monolog.formatter.json
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine"]
deprecation:
type: stream
channels: [deprecation]
path: php://stderr

View File

@ -0,0 +1,16 @@
framework:
notifier:
#chatter_transports:
# slack: '%env(SLACK_DSN)%'
# telegram: '%env(TELEGRAM_DSN)%'
#texter_transports:
# twilio: '%env(TWILIO_DSN)%'
# nexmo: '%env(NEXMO_DSN)%'
channel_policy:
# use chat/slack, chat/telegram, sms/twilio or sms/nexmo
urgent: ['email']
high: ['email']
medium: ['email']
low: ['email']
admin_recipients:
- { email: admin@example.com }

View File

@ -0,0 +1,3 @@
sensio_framework_extra:
router:
annotations: false

View File

@ -0,0 +1,15 @@
when@dev:
web_profiler:
toolbar: true
intercept_redirects: false
framework:
profiler: { only_exceptions: false }
when@test:
web_profiler:
toolbar: false
intercept_redirects: false
framework:
profiler: { collect: false }

View File

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

14
controllers.json Normal file
View File

@ -0,0 +1,14 @@
{
"controllers": {
"@symfony/ux-dropzone": {
"dropzone": {
"enabled": true,
"fetch": "eager",
"autoimport": {
"@symfony/ux-dropzone/src/style.css": true
}
}
}
},
"entrypoints": []
}

View File

@ -0,0 +1,14 @@
version: '3'
services:
###> doctrine/doctrine-bundle ###
database:
ports:
- "5432"
###< doctrine/doctrine-bundle ###
###> symfony/mailer ###
mailer:
image: schickling/mailcatcher
ports: [1025, 1080]
###< symfony/mailer ###

21
docker-compose.yml Normal file
View File

@ -0,0 +1,21 @@
version: '3'
services:
###> doctrine/doctrine-bundle ###
database:
image: postgres:${POSTGRES_VERSION:-13}-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB:-app}
# You should definitely change the password in production
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-ChangeMe}
POSTGRES_USER: ${POSTGRES_USER:-symfony}
volumes:
- db-data:/var/lib/postgresql/data:rw
# You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data!
# - ./docker/db/data:/var/lib/postgresql/data:rw
###< doctrine/doctrine-bundle ###
volumes:
###> doctrine/doctrine-bundle ###
db-data:
###< doctrine/doctrine-bundle ###

View File

@ -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 Version20220409135404 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 first_name VARCHAR(255) DEFAULT NULL, ADD last_name VARCHAR(255) DEFAULT 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 first_name, DROP last_name');
}
}

View File

@ -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 Version20220409150258 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('CREATE TABLE quotes (id INT AUTO_INCREMENT NOT NULL, quote LONGTEXT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE quotes');
}
}

View File

@ -0,0 +1,33 @@
<?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 Version20220410123550 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('CREATE TABLE projects (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL, url VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE user CHANGE first_name first_name VARCHAR(255) DEFAULT NULL');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE projects');
$this->addSql('ALTER TABLE `user` CHANGE first_name first_name VARCHAR(255) DEFAULT \'\'');
}
}

View File

@ -0,0 +1,35 @@
<?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 Version20220410131552 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('CREATE TABLE projects_user (projects_id INT NOT NULL, user_id INT NOT NULL, INDEX IDX_B38D6A811EDE0F55 (projects_id), INDEX IDX_B38D6A81A76ED395 (user_id), PRIMARY KEY(projects_id, user_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE projects_user ADD CONSTRAINT FK_B38D6A811EDE0F55 FOREIGN KEY (projects_id) REFERENCES projects (id) ON DELETE CASCADE');
$this->addSql('ALTER TABLE projects_user ADD CONSTRAINT FK_B38D6A81A76ED395 FOREIGN KEY (user_id) REFERENCES `user` (id) ON DELETE CASCADE');
$this->addSql('ALTER TABLE projects ADD teaser_image VARCHAR(255) DEFAULT NULL');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE projects_user');
$this->addSql('ALTER TABLE projects DROP teaser_image');
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Controller\Admin;
use App\Entity\Projects;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
/**
*
*/
class ProjectsCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return Projects::class;
}
/*
public function configureFields(string $pageName): iterable
{
return [
IdField::new('id'),
TextField::new('title'),
TextEditorField::new('description'),
];
}
*/
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Controller\Admin;
use App\Entity\Quotes;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
/**
*
*/
class QuotesCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return Quotes::class;
}
public function configureFields(string $pageName): iterable
{
return [
IdField::new(propertyName: 'id'),
TextField::new(propertyName: 'quote'),
];
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace App\Controller;
use App\Repository\ProjectsRepository;use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
*
*/
class ProjectsController extends AbstractController
{
#[Route(path: '/projects/{name}', name: 'app_projects')]
public function index(ProjectsRepository $projectsRepository, string $name = ''): Response
{
if ($name == '') {
return $this->render(view: 'projects/index.html.twig', parameters: [
'projects' => $projectsRepository->findAll()
]);
} else {
if ($project = $projectsRepository->findOneByName(value: $name)) {
$readMe = file_get_contents(filename: $project->getURL() . '/raw/branch/master/README.md');
//$parsedReadMe = $markdownParser->transformMarkdown(text: $readMe);
return $this->render(view: 'projects/show.html.twig', parameters: [
'project' => $project,
'readme' => $readMe
]);
} else {
throw $this->createNotFoundException();
}
}
}
}

View File

@ -2,17 +2,74 @@
namespace App\Controller; namespace App\Controller;
use App\Entity\User;
use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
/**
* Class UserController
* @package App\Controller
*/
class UserController extends AbstractController class UserController extends AbstractController
{ {
#[Route('/user', name: 'user')] /**
public function index(): Response * @param \App\Repository\UserRepository $userRepository
* @param string $userName
*
* @return \Symfony\Component\HttpFoundation\Response
*/
#[Route(path: '/profile/edit/{username}', name: 'app_profile_edit')]
public function editProfile(UserRepository $userRepository, string $username = ''): Response
{ {
return $this->render('user/index.html.twig', [ /** var User $user */
'controller_name' => 'UserController', if ($username === '') {
if ($this->isGranted(attribute: 'ROLE_USER')) {
$user = $this->getUser();
} else {
throw new AccessDeniedException('You need to be logged in.');
}
} else {
if ($this->isGranted(attribute: 'ROLE_ADMIN')) {
$user = $userRepository->findOneBy([
"username" => $username
]); ]);
} }
}
if (isset($user)) {
return $this->render(view: 'user/edit_profile.html.twig', parameters: [
'user' => $user,
]);
} else {
throw new UserNotFoundException();
}
}
/**
* @param \App\Repository\UserRepository $userRepository
* @param string $username
*
* @return \Symfony\Component\HttpFoundation\Response
*/
#[Route(path: '/profile/{username}', name: 'app_profile')]
public function showProfile(UserRepository $userRepository, string $username = ''): Response
{
/** var User $user */
if ($username === '') {
$user = $this->getUser();
} else {
$user = $userRepository->findOneBy([
"username" => $username
]);
}
return $this->render(view: 'user/show_profile.html.twig', parameters: [
'user' => $user,
]);
}
} }

140
src/Entity/Projects.php Normal file
View File

@ -0,0 +1,140 @@
<?php
namespace App\Entity;
use App\Repository\ProjectsRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
*
*/
#[ORM\Entity(repositoryClass: ProjectsRepository::class)]
class Projects
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private ?int $id;
#[ORM\Column(type: 'string', length: 255)]
private ?string $name;
#[ORM\Column(type: 'string', length: 255)]
private ?string $description;
#[ORM\Column(type: 'string', length: 255)]
private ?string $url;
#[ORM\Column(type: 'datetime_immutable')]
private ?\DateTimeImmutable $createdAt;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private ?string $teaserImage;
#[ORM\ManyToMany(targetEntity: User::class, inversedBy: 'projects')]
private $developer;
public function __construct()
{
$this->developer = new ArrayCollection();
}
/**
* @return null|string
*/
public function __toString()
{
return $this->name;
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $Name): self
{
$this->name = $Name;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(string $Description): self
{
$this->description = $Description;
return $this;
}
public function getURL(): ?string
{
return $this->url;
}
public function setURL(string $url): self
{
$this->url = $url;
return $this;
}
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeImmutable $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getTeaserImage(): ?string
{
return $this->teaserImage;
}
public function setTeaserImage(?string $teaserImage): self
{
$this->teaserImage = $teaserImage;
return $this;
}
/**
* @return Collection<int, User>
*/
public function getDeveloper(): Collection
{
return $this->developer;
}
public function addDeveloper(User $developer): self
{
if (!$this->developer->contains($developer)) {
$this->developer[] = $developer;
}
return $this;
}
public function removeDeveloper(User $developer): self
{
$this->developer->removeElement($developer);
return $this;
}
}

35
src/Entity/Quotes.php Normal file
View File

@ -0,0 +1,35 @@
<?php
namespace App\Entity;
use App\Repository\QuotesRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: QuotesRepository::class)]
class Quotes
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'text')]
private $quote;
public function getId(): ?int
{
return $this->id;
}
public function getQuote(): ?string
{
return $this->quote;
}
public function setQuote(string $quote): self
{
$this->quote = $quote;
return $this;
}
}

View File

@ -0,0 +1,73 @@
<?php
namespace App\Repository;
use App\Entity\Projects;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method Projects|null find($id, $lockMode = null, $lockVersion = null)
* @method Projects|null findOneBy(array $criteria, array $orderBy = null)
* @method Projects[] findAll()
* @method Projects[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ProjectsRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct(registry: $registry, entityClass: Projects::class);
}
/**
*/
public function add(Projects $entity, bool $flush = true): void
{
$this->_em->persist($entity);
if ($flush) {
$this->_em->flush();
}
}
/**
*/
public function remove(Projects $entity, bool $flush = true): void
{
$this->_em->remove($entity);
if ($flush) {
$this->_em->flush();
}
}
// /**
// * @return Projects[] Returns an array of Projects objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('p')
->andWhere('p.exampleField = :val')
->setParameter('val', $value)
->orderBy('p.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
public function findOneByName($value): ?Projects
{
try {
return $this->createQueryBuilder(alias: 'q')
->andWhere('q.name = :val')
->setParameter(key: 'val', value: $value)
->getQuery()
->getOneOrNullResult();
} catch(NonUniqueResultException $e) {
dd($e->getMessage());
}
}
}

View File

@ -0,0 +1,94 @@
<?php
namespace App\Repository;
use App\Entity\Quotes;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\ORMException;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method Quotes|null find($id, $lockMode = null, $lockVersion = null)
* @method Quotes|null findOneBy(array $criteria, array $orderBy = null)
* @method Quotes[] findAll()
* @method Quotes[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class QuotesRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct(registry: $registry, entityClass: Quotes::class);
}
/**
*/
public function add(Quotes $entity, bool $flush = true): void
{
$this->_em->persist($entity);
if ($flush) {
$this->_em->flush();
}
}
/**
*/
public function remove(Quotes $entity, bool $flush = true): void
{
$this->_em->remove($entity);
if ($flush) {
$this->_em->flush();
}
}
/**
* @return null|float|int|mixed|string
* @throws \Doctrine\ORM\NonUniqueResultException
*/
public function findOneRandom(): mixed
{
$idLimits = $this->createQueryBuilder(alias: 'q')
->select('MIN(q.id)', 'MAX(q.id)')
->getQuery()
->getOneOrNullResult();
$randomPossibleId = rand(min: $idLimits[1], max: $idLimits[2]);
return $this->createQueryBuilder(alias: 'q')
->where(predicates: 'q.id >= :random_id')
->setParameter(key: 'random_id', value: $randomPossibleId)
->setMaxResults(maxResults: 1)
->getQuery()
->getOneOrNullResult();
}
// /**
// * @return Quotes[] Returns an array of Quotes objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('q')
->andWhere('q.exampleField = :val')
->setParameter('val', $value)
->orderBy('q.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?Quotes
{
return $this->createQueryBuilder('q')
->andWhere('q.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -0,0 +1,97 @@
<?php
namespace App\Security;
use App\Entity\User;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use App\Repository\UserRepository;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
use function mysql_xdevapi\getSession;
/**
*
*/
class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
/**
* @var \App\Repository\UserRepository
*/
private UserRepository $userRepository;
/**
* @var \Symfony\Component\Routing\RouterInterface
*/
private RouterInterface $router;
public function __construct(UserRepository $userRepository, RouterInterface $router)
{
$this->userRepository = $userRepository;
$this->router = $router;
}
public function authenticate(Request $request): Passport
{
$username = $request->request->get(key: 'username');
$password = $request->request->get(key: 'password');
$csrfToken = $request->request->get(key: '_csrf_token');
$request->getSession()->set(name: Security::LAST_USERNAME, value: $username);
return new Passport(
userBadge: new UserBadge(userIdentifier: $username, userLoader: function ($userIdentifier) {
$user = $this->userRepository->findOneBy(['username' => $userIdentifier]);
if (!$user) {
$user = $this->userRepository->findOneBy(['email' => $userIdentifier]);
}
if (!$user) {
throw new UserNotFoundException();
}
return $user;
}),
credentials: new CustomCredentials(customCredentialsChecker: function ($credentials, User $user) {
return $credentials === 'test';
}, credentials : $password),
// new PasswordCredentials($password),
badges: [
new CsrfTokenBadge(csrfTokenId: 'authenticate', csrfToken: $csrfToken),
new RememberMeBadge()
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
if ($target = $this->getTargetPath(session: $request->getSession(), firewallName: $firewallName)) {
return new RedirectResponse(url: $target);
}
return new RedirectResponse(
url: $this->router->generate(name: 'app_main')
);
}
protected function getLoginUrl(Request $request): string
{
return $this->router->generate(name: 'app_login');
}
}

View File

@ -0,0 +1,3 @@
{# templates/admin/index.html.twig #}
{% extends '@EasyAdmin/page/content.html.twig' %}

View File

@ -0,0 +1,70 @@
{# templates/projects/index.html.twig #}
{% extends 'base.html.twig' %}
{% block title %} Projects {% endblock %}
{% block body %}
<div class="container-fluid box">
<div class="row">
<h2>This is an overview of my current public (and open source) projects.</h2>
<!-- projects List -->
<div class="col-sm-12">
{% for project in projects %}
<div class="project-container bg-dark my-4">
<div class="row">
<div class="col-sm-3">
<a href="{{ path('app_projects', { name: project.name }) }}">
{% if project.teaserImage %}
<img
class="blog-img"
src=" {# uploaded_asset(blogpost.teaserImage)|imagine_filter('squared_thumbnail_small') #}"
alt="Teaser">
{% else %}
<img
class="blog-img"
src="{{ asset('build/images/24unix/24_logo_bg_96x96.png') }}"
alt="Teaser">
{% endif %}
</a>
<br>
<div>
{% for developer in project.developer %}
<a class="align-left blog-details"
href="{{ path('app_profile', { 'username':developer.username }) }}">
<img class="article-author-img rounded-circle"
src="{{ asset('build/images/tracer_schmolle.png') }}"
alt="profile"></a>
<a href="{{ path('app_profile', { 'username':developer.username }) }}">{{ developer.username }}</a>
{% endfor %}
</div>
</div>
<div class="col-sm-8 mt-2">
<a href="{{ path('app_projects', { name: project.name }) }}">
<div class="article-title d-inline-block pl-3 align-middle">
<h2>{{ project.name }}</h2>
</div>
</a>
<br>
<div class="blog-teaser mb-2 pb-2 text-xl-start">
{{ project.description }}
<br>
<br>
started: {{ project.createdAt | ago }}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% if is_granted('ROLE_ADMIN') %}
<div class="text-xl-start">
<a href="{{ path('app_main') }}"><i class="fa fa-plus-circle"></i></a>
</div>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,38 @@
{# templates/blog/blog_show.html.twig #}
{% extends 'base.html.twig' %}
{% block title %} Blogpost {% endblock %}
{% block body %}
<div class="row">
<div class="col-sm-12">
{% if is_granted('ROLE_ADMIN') %}
<div class="d-flex justify-content-end">
<a href="{{ path('app_main', { id : project.id }) }}"><i class="fa fa-3x fa-fw fa-edit"></i></a>
<a href="{{ path('app_main', { id : project.id }) }}"><i class="fa fa-3x fa-fw fa-trash"></i></a>
</div>
{% endif %}
<div class="show-article-container p-3 mt-4">
<div class="show-article-title-container d-inline-block pl-3 align-middle">
<h2>{{ project.name }}</h2>
</div>
<div>
Source: <a href="{{ project.url }}" target="_blank">{{ project.url }}</a>&nbsp;<i
class="fa fa-external-link" aria-hidden="true"></i>
</div>
<div class="row">
<div class="col-sm-12">
<div class="article-text">
{{ readme | markdown_to_html }}
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,53 @@
{% extends 'base.html.twig' %}
{% block title %}
Profile of {{ user.username }}
{% endblock %}
{% block body %}
<div class="container box rounded bg-dark mt-5 mb-5">
<div class="row">
<div class="col-md-3 border-right">
<div class="d-flex flex-column align-items-center text-center p-3 py-5">
<img class="rounded-circle mt-5"
width="150px"
src=" {{ asset('build/images/tracer_schmolle.png') }}" alt="profile image">
<span class="font-weight-bold">{{ user.username }}</span>
<span class="text-white-50"><i class="fa fa-lg fa-envelope me-1"></i>{{ user.email }}</span>
</div>
</div>
<div class="col-md-5 border-right">
<div class="p-3 py-5">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="text-right">User Profile</h4>
</div>
<div class="row mt-2">
<div class="col-md-6">
<label class="labels" for="first-name">First Name</label>
<input type="text" id="first-name" class="form-control" placeholder="First Name" value="{{ user.firstName }}">
</div>
<div class="col-md-6">
<label class="labels" for="last-name">Last Name</label>
<input type="text" id="last-name" class="form-control" value="{{ user.lastName }}" placeholder="Last Name">
</div>
</div>
<div class="col-md-6">
<label class="labels" for="username">Username</label>
<input type="text" class="form-control" id="username" placeholder="eMail address" value="{{ user.email }}">
</div>
<!--
<div class="mt-5 text-center">
<button class="btn btn-primary profile-button" type="button">Save Profile</button>
</div>
-->
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,53 @@
{% extends 'base.html.twig' %}
{% block title %}
Profile of {{ user.username }}
{% endblock %}
{% block body %}
<div class="container box rounded bg-dark mt-5 mb-5">
<div class="row">
<div class="col-md-3 border-right">
<div class="d-flex flex-column align-items-center text-center p-3 py-5">
<img class="rounded-circle mt-5"
width="150px"
src=" {{ asset('build/images/tracer_schmolle.png') }}" alt="profile image">
<span class="font-weight-bold">{{ user.username }}</span>
<span class="text-white-50"><i class="fa fa-lg fa-envelope me-1"></i>{{ user.email }}</span>
</div>
</div>
<div class="col-md-5 border-right">
<div class="p-3 py-5">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="text-right">User Profile</h4>
</div>
<div class="row mt-2">
<div class="col-md-6">
<label class="labels" for="first-name">First Name</label>
<input type="text" disabled id="first-name" class="form-control" placeholder="First Name" value="{{ user.firstName }}">
</div>
<div class="col-md-6">
<label class="labels" for="last-name">Last Name</label>
<input type="text" disabled id="last-name" class="form-control" value="{{ user.lastName }}" placeholder="Last Name">
</div>
</div>
<div class="col-md-6">
<label class="labels" for="username">Username</label>
<input type="text" disabled class="form-control" id="username" placeholder="eMail address" value="{{ user.email }}">
</div>
<!--
<div class="mt-5 text-center">
<button class="btn btn-primary profile-button" type="button">Save Profile</button>
</div>
-->
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,2 @@
"Invalid credentials.": "Invalid password entered!"
"Username could not be found.": "Neither user or eMail address is valid!"