diff --git a/README.md b/README.md index 53849d4..809e5f7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -TODO: +A CMS based on symfony -Make user work -make quotes work \ No newline at end of file +WIP \ No newline at end of file diff --git a/TODO b/TODO index b663457..7d3b7fd 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,9 @@ accent color #d43934 -merge RequestController/SecurityController -don't use html in easyadmin / quotes +add dates and author to pages + use turbo make unit tests -- harden Api-Platform diff --git a/assets/app.js b/assets/app.js index bb0a6aa..48617e4 100644 --- a/assets/app.js +++ b/assets/app.js @@ -6,7 +6,26 @@ */ // any CSS you import will output into a single css file (app.css in this case) -import './styles/app.css'; +import 'fork-awesome/scss/fork-awesome.scss' +import './styles/app.scss' +import $ from 'jquery' +import 'bootstrap' +//import './js/index' +// needed for legacy code +//global.$ = $ +if (window.matchMedia('(prefers-color-scheme)').media !== 'not all') { + console.log('🎉 Dark mode is supported') +} + +$(document).ready(() => { + console.log('ready') + $('#toggleSidebar').on('click', () => { + const toggleIcon = $('#toggleIcon') + toggleIcon.toggleClass('fa fa-lg fa-fw fa-caret-square-o-left') + toggleIcon.toggleClass('fa fa-lg fa-fw fa-caret-square-o-right') + $('#sidebar').toggleClass('active') + }) +}) // start the Stimulus application -import './bootstrap'; +//import './bootstrap' diff --git a/assets/bootstrap.js b/assets/bootstrap.js index 4ab2df6..8baa386 100644 --- a/assets/bootstrap.js +++ b/assets/bootstrap.js @@ -1,11 +1,11 @@ -import { startStimulusApp } from '@symfony/stimulus-bridge'; +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, /\.[jt]sx?$/ -)); +)) // register any custom, 3rd party controllers here // app.register('some_controller_name', SomeImportedController); diff --git a/assets/js/index.js b/assets/js/index.js index 0956617..ac45951 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -1,17 +1,2 @@ // assets/js/index.js -import '../styles/app.scss' -import 'fork-awesome/scss/fork-awesome.scss' - -require('bootstrap') -import 'bootswatch/dist/slate/bootstrap.min.css' - -$(document).ready(function () { - console.log('ready') - $('#toggleSidebar').on('click', function () { - let toggleIcon = $('#toggleIcon') - toggleIcon.toggleClass('fa fa-lg fa-fw fa-caret-square-o-left') - toggleIcon.toggleClass('fa fa-lg fa-fw fa-caret-square-o-right') - $('#sidebar').toggleClass('active') - }) -}) diff --git a/assets/styles/app.scss b/assets/styles/app.scss index da8b002..bf78d99 100644 --- a/assets/styles/app.scss +++ b/assets/styles/app.scss @@ -14,6 +14,15 @@ //border: 1px solid gray; } +// customize some Bootstrap variables +$primary: #FF8040; +$jet-black: #0e0e10; + +//$body-bg: red; +$body-color: #8f8f8f; + +@import 'bootstrap-dark-5/dist/css/bootstrap-night.min.css'; + html, body { margin: 0; @@ -23,10 +32,6 @@ html, body { } -// customize some Bootstrap variables -$primary: #FF8040; -$jet-black: #0e0e10; -//@import "bootstrap/scss/bootstrap"; /// @@ -40,6 +45,7 @@ $jet-black: #0e0e10; white-space: nowrap; overflow: hidden; float: left; + background-color: $jet-black; transition: min-width 0.5s, max-width 0.5s; } @@ -57,7 +63,7 @@ $jet-black: #0e0e10; #main_content { overflow-y: auto; - height: calc(100vh - 160px) + height: calc(100vh - 180px); // 74px header, 64px header } .form-signin { @@ -96,3 +102,13 @@ $jet-black: #0e0e10; margin-bottom: 10px; border-radius: 0 0 0.375rem 0.375rem; } + +.quote-box { + border-left: 1px solid #ff8844; + color: #ff8844; +} + +.project-image { + width: 200px; + margin: 15px; +} \ No newline at end of file diff --git a/package.json b/package.json index 0e0ac3e..129362e 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@typescript-eslint/eslint-plugin-tslint": "^5.26.0", "axios": "^0.27.1", "bootstrap": "^5.2.2", + "bootstrap-dark-5": "^1.1.3", "bootswatch": "^5.2.2", "eslint": "^8.15.0", "eslint-config-airbnb-base": "^15.0.0", diff --git a/src/Controller/Admin/DashboardController.php b/src/Controller/Admin/DashboardController.php index 2f00865..b50af3f 100644 --- a/src/Controller/Admin/DashboardController.php +++ b/src/Controller/Admin/DashboardController.php @@ -52,7 +52,7 @@ class DashboardController extends AbstractDashboardController } return parent::configureUserMenu(user: $user) - ->setAvatarUrl(url: 'build/images/'.$user->getAvatar()); + ->setAvatarUrl(url: 'uploads/avatars/' . $user->getAvatar()); } public function configureActions(): Actions diff --git a/src/Controller/Admin/PagesCrudController.php b/src/Controller/Admin/PagesCrudController.php index 36a8d1d..8aad8fb 100644 --- a/src/Controller/Admin/PagesCrudController.php +++ b/src/Controller/Admin/PagesCrudController.php @@ -8,6 +8,7 @@ use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField; use EasyCorp\Bundle\EasyAdminBundle\Field\CodeEditorField; use EasyCorp\Bundle\EasyAdminBundle\Field\IdField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField; +use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; class PagesCrudController extends AbstractCrudController @@ -24,7 +25,7 @@ class PagesCrudController extends AbstractCrudController yield TextField::new(propertyName: 'name'); yield AssociationField::new(propertyName: 'owner'); // yield CodeEditorField::new(propertyName: 'content') - yield TextareaField::new(propertyName: 'content') + yield TextEditorField::new(propertyName: 'content') ->onlyOnForms(); } } diff --git a/src/Controller/Admin/QuotesCrudController.php b/src/Controller/Admin/QuotesCrudController.php index d371584..db59240 100644 --- a/src/Controller/Admin/QuotesCrudController.php +++ b/src/Controller/Admin/QuotesCrudController.php @@ -5,7 +5,7 @@ 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\TextareaField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; class QuotesCrudController extends AbstractCrudController @@ -21,7 +21,7 @@ class QuotesCrudController extends AbstractCrudController ->onlyOnIndex(); yield TextField::new(propertyName: 'quote') ->onlyOnIndex(); - yield TextEditorField::new(propertyName: 'quote') + yield TextAreaField::new(propertyName: 'quote') ->onlyOnForms(); } } diff --git a/src/Controller/Admin/UserCrudController.php b/src/Controller/Admin/UserCrudController.php index 75e0c3c..4efae24 100644 --- a/src/Controller/Admin/UserCrudController.php +++ b/src/Controller/Admin/UserCrudController.php @@ -27,7 +27,7 @@ class UserCrudController extends AbstractCrudController yield ImageField::new(propertyName: 'avatar') ->setBasePath(path: 'uploads/avatars') ->setUploadDir(uploadDirPath: 'public/uploads/avatars') - ->setUploadedFileNamePattern(patternOrCallable: '[timestamp]-[slug].[extension]'); + ->setUploadedFileNamePattern(patternOrCallable: '[slug]-[timestamp].[extension]'); $roles = ['ROLE_FOUNDER', 'ROLE_ADMIN', 'ROLE_MODERATOR', 'ROLE_USER']; yield ChoiceField::new(propertyName: 'roles') ->setChoices(choiceGenerator: array_combine(keys: $roles, values: $roles)) diff --git a/src/Controller/FrontendController.php b/src/Controller/FrontendController.php index 50f07f8..9064422 100644 --- a/src/Controller/FrontendController.php +++ b/src/Controller/FrontendController.php @@ -23,7 +23,7 @@ class FrontendController extends AbstractController return $this->render(view: '@default/base.html.twig', parameters: [ 'user' => $user, - 'quote' => json_encode(value: $quote->getQuote()) + 'quote' => $quote->getQuote() ]); } } diff --git a/src/Controller/PagesController.php b/src/Controller/PagesController.php index 6638c81..bab1948 100644 --- a/src/Controller/PagesController.php +++ b/src/Controller/PagesController.php @@ -4,39 +4,37 @@ namespace App\Controller; use App\Entity\Pages; use App\Repository\PagesRepository; +use App\Repository\UserRepository; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; class PagesController extends AbstractController { - #[Route(path: '/pages/{name}', name: 'pages_display')] - public function display(PagesRepository $pagesRepository, string $name): Response + #[Route(path: '/pages/{slug}', name: 'pages_display')] + public function display(PagesRepository $pagesRepository, UserRepository $userRepository, string $slug): Response { $page = $pagesRepository->findOneBy([ - 'slug' => $name, + 'slug' => $slug, ]); if (!$page) { $page = new Pages(); $page->setName(name: 'Not Found'); - $page->setContent(content: 'The requested page was not found.'); + $page->setContent(content: '404 - The requested page was not found.'); } - return $this->render(view: '@default/pages/display.html.twig', parameters: [ - 'page' => $page, + dump($page); + dump($page->getOwner()); + $user = $userRepository->findOneBy(['id' => $page->getOwner()->getId()]); + dd($user); + + return $this->render(view: '@default/pages/index.html.twig', parameters: [ + 'page_name' => $page->getName(), + 'page_content' => $page->getContent(), + 'created_at' => $page->getCreatedAt(), + 'modified_at' => $page->getModifiedAt(), + 'owner' => $page->getOwner() ]); } - - #[Route(path: '/imprint', name: 'app_imprint')] - public function imprint(): Response - { - return $this->render(view: '@default/pages/imprint.html.twig'); - } - - #[Route(path: '/privacy', name: 'app_privacy')] - public function privacy(): Response - { - return $this->render(view: '@default/pages/privacy.html.twig'); - } } diff --git a/src/Entity/Pages.php b/src/Entity/Pages.php index dbff5bb..4956c17 100644 --- a/src/Entity/Pages.php +++ b/src/Entity/Pages.php @@ -2,56 +2,38 @@ namespace App\Entity; -use ApiPlatform\Core\Annotation\ApiFilter; -use ApiPlatform\Core\Annotation\ApiResource; -use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter; use App\Repository\PagesRepository; use DateTimeImmutable; use Doctrine\ORM\Mapping as ORM; use Sunrise\Slugger\Slugger; #[ORM\Entity(repositoryClass: PagesRepository::class), ORM\HasLifecycleCallbacks] -#[ApiResource] -#[ApiFilter(filterClass: SearchFilter::class, properties: ['slug' => 'exact'])] class Pages { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: 'integer')] - private $id; + private ?int $id; #[ORM\Column(type: 'string', length: 255)] - private $name; + private string $name; #[ORM\Column(type: 'text')] - private $content; + private string $content; #[ORM\Column(type: 'datetime_immutable')] - private $createdAt; + private ?DateTimeImmutable $createdAt; #[ORM\Column(type: 'datetime_immutable', nullable: true)] - private $modifiedAt; + private ?DateTimeImmutable $modifiedAt; #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'pages')] #[ORM\JoinColumn(nullable: false)] - private $owner; + private User $owner; #[ORM\Column(type: 'string', length: 255)] - private $slug; - - /** - * @param $id - */ - - /* public function __construct(String $name = '', String $content = '') - { - $this->name = $name; - $this->content = $content; - $owner = $userRepository->findOneBy(['username' => 'tracer']); - $this->owner = $owner; - } - */ + private string $slug; public function getId(): ?int { diff --git a/src/Entity/Quotes.php b/src/Entity/Quotes.php index 7910b25..a1e03be 100644 --- a/src/Entity/Quotes.php +++ b/src/Entity/Quotes.php @@ -2,21 +2,19 @@ namespace App\Entity; -use ApiPlatform\Core\Annotation\ApiResource; use App\Repository\QuotesRepository; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: QuotesRepository::class)] -#[ApiResource] class Quotes { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: 'integer')] - private $id; + private int $id; #[ORM\Column(type: 'text')] - private $quote; + private string $quote; public function getId(): ?int { diff --git a/src/Entity/User.php b/src/Entity/User.php index e8b05fc..8540453 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -3,11 +3,11 @@ namespace App\Entity; use App\Repository\UserRepository; +use DateTime; use DateTimeImmutable; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use Stringable; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -15,7 +15,6 @@ use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: UserRepository::class), ORM\HasLifecycleCallbacks] #[UniqueEntity(fields: ['username', 'email'], message: 'There is already an account with this username or email.')] - class User implements UserInterface, PasswordAuthenticatedUserInterface { #[ORM\Id] @@ -45,22 +44,22 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface private ?string $lastName = ''; #[ORM\ManyToMany(targetEntity: Projects::class, mappedBy: 'developer')] - private $projects; + private Collection $projects; #[ORM\Column(type: 'string', length: 255, nullable: true)] - private string $avatar; + private ?string $avatar; #[ORM\OneToMany(mappedBy: 'owner', targetEntity: Pages::class)] - private $pages; + private Collection $pages; #[ORM\Column(type: 'datetime_immutable')] - private $createdAt; + private DateTimeImmutable $createdAt; #[ORM\Column(type: 'datetime_immutable', nullable: true)] - private $modifiedAt; + private DateTimeImmutable $modifiedAt; #[ORM\Column(type: 'boolean')] - private $isVerified = false; + private bool $isVerified = false; #[ORM\Column] private ?DateTimeImmutable $agreedTermsAt = null; @@ -137,13 +136,6 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface return $this; } - /** - * @see UserInterface - */ - public function eraseCredentials() - { - $this->plainPassword = ''; - } public function getEmail(): ?string { @@ -157,10 +149,6 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface return $this; } - public function setPlainPassword(string $plainPassword): void - { - $this->plainPassword = $plainPassword; - } public function getFirstName(): ?string { @@ -278,14 +266,14 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface return $this; } - - /** - * @return string - */ - public function getAvatarUri() - { - return 'avatar'; - } + + /** + * @return string + */ + public function getAvatarUri(): string + { + return 'avatar'; + } #[ORM\PrePersist] public function onPrePersist(): void @@ -322,4 +310,9 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface return $this; } + + public function eraseCredentials() + { + // TODO: Implement eraseCredentials() method. + } } diff --git a/src/Repository/PagesRepository.php b/src/Repository/PagesRepository.php index 933342f..db741c6 100644 --- a/src/Repository/PagesRepository.php +++ b/src/Repository/PagesRepository.php @@ -23,7 +23,7 @@ class PagesRepository extends ServiceEntityRepository public function add(Pages $entity, bool $flush = true): void { - $this->_em->persist($entity); + $this->_em->persist(entity: $entity); if ($flush) { $this->_em->flush(); } @@ -31,7 +31,7 @@ class PagesRepository extends ServiceEntityRepository public function remove(Pages $entity, bool $flush = true): void { - $this->_em->remove($entity); + $this->_em->remove(entity: $entity); if ($flush) { $this->_em->flush(); } diff --git a/templates/themes/default/_footer.html.twig b/templates/themes/default/_footer.html.twig index b1cc8e1..a7208df 100644 --- a/templates/themes/default/_footer.html.twig +++ b/templates/themes/default/_footer.html.twig @@ -1,12 +1,14 @@