diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3e3af47 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "sqltools.connections": [ + { + "previewLimit": 50, + "driver": "SQLite", + "name": "dbapplication", + "database": "${workspaceFolder:Fukafukashita}/var/data.db" + } + ] +} \ No newline at end of file diff --git a/app.session.sql b/app.session.sql new file mode 100644 index 0000000..f25d5fa --- /dev/null +++ b/app.session.sql @@ -0,0 +1,7 @@ +INSERT INTO profil (id, name, description, password) +VALUES ( + 7, + 'name:VARCHAR(255)', + 'description:VARCHAR(255)', + 'password:VARCHAR(255)' + ); \ No newline at end of file diff --git a/assets/scripts/scripts.js b/assets/scripts/scripts.js new file mode 100644 index 0000000..599fcb7 --- /dev/null +++ b/assets/scripts/scripts.js @@ -0,0 +1,17 @@ +// public/js/scripts.js + +function openPopup() { + document.getElementById("followPopup").style.display = "block"; +} + +function closePopup() { + document.getElementById("followPopup").style.display = "none"; +} + +// Close the popup if the user clicks outside of it +window.onclick = function(event) { + var popup = document.getElementById("followPopup"); + if (event.target == popup) { + popup.style.display = "none"; + } +} diff --git a/assets/styles/app.css b/assets/styles/app.css index dd6181a..08bac17 100644 --- a/assets/styles/app.css +++ b/assets/styles/app.css @@ -1,3 +1,162 @@ body { - background-color: skyblue; + font-family: Arial, sans-serif; + /* background-color: #f0f0f0; */ + color: #1a2c4c; } + + +.profile-container { + background-color: #fff; + border: 1px solid #5c5d7f; + border-radius: 8px; + max-width: 600px; + margin: 20px auto; + padding: 20px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + } + + .profile-header { + display: flex; + align-items: center; + border-bottom: 1px solid #5c5d7f; + padding-bottom: 10px; + margin-bottom: 20px; + } + + .profile-image { + border-radius: 50%; + width: 100px; + height: 100px; + object-fit: cover; + margin-right: 20px; +} + +.profile-info { + flex-grow: 1; + } + + .profile-info h1 { + font-size: 24px; + margin: 0; + color: #1a2c4c; + } + +.profile-info p { + margin: 5px 0; + color: #38476b; +} + +.horizontal-layout { + display: flex; + align-items: center; +} + +.follow-button { + background-color: #38476b; + color: #fff; + padding: 10px 20px; + border: none; + border-radius: 4px; + cursor: pointer; + text-align: center; + text-decoration: none; + margin: 4px; +} + +.delete-button { + background-color: #970c2a; + color: #fff; + padding: 10px 20px; + border: none; + border-radius: 4px; + cursor: pointer; + text-align: center; + text-decoration: none; + margin: 4px; +} +.count-container { + display: flex; + margin-left: auto; /* Pushes the container to the right */ +} + +.count-button { + background-color: #38476b; + color: #fff; + padding: 10px 20px; + border: none; + border-radius: 4px; + cursor: pointer; + text-align: center; + text-decoration: none; + margin: 4px; +} + + + +.count-button:hover{ + background-color: #5c5d7f; +} + +.follow-button:hover { + background-color: #5c5d7f; +} + + +/* public/css/styles.css */ + +/* Ajoutez les styles existants ici */ + +.popup { + display: none; /* Hidden by default */ + position: fixed; + z-index: 1; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgb(0,0,0); + background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ +} + +.popup-content { + background-color: #fefefe; + margin: 15% auto; + padding: 20px; + border: 1px solid #888; + width: 80%; + max-width: 400px; + border-radius: 8px; + text-align: center; +} + +.close { + color: #aaa; + float: right; + font-size: 28px; + font-weight: bold; +} + +.close:hover, +.close:focus { + color: black; + text-decoration: none; + cursor: pointer; +} + +html { + --s: 257px; + /* control the size */ + --c1: #38476b; + --c2: #bda3b6; + + --_c: var(--c1) calc(100% - var(--s)/2) 99%, #0000; + --_g: var(--s), #0000 calc(99% - var(--s)/2), var(--_c); + background: + radial-gradient(var(--s) at 100% var(--_g)), + radial-gradient(calc(var(--s)/4) at 50% calc(100%/3), var(--_c)) var(--s) 0, + radial-gradient(var(--s) at 0% var(--_g)) 0 calc(3*var(--s)) var(--c2); + background-size: + calc(2*var(--s)) calc(9*var(--s)/4), + calc(2*var(--s)) calc(3*var(--s)/4); + } \ No newline at end of file diff --git a/composer.json b/composer.json index 759ce3c..15798fa 100644 --- a/composer.json +++ b/composer.json @@ -43,6 +43,8 @@ "symfony/web-link": "7.0.7", "symfony/yaml": "7.0.7", "twig/extra-bundle": "^2.12|^3.0", + "twig/intl-extra": "^3.10", + "twig/string-extra": "^3.10", "twig/twig": "^2.12|^3.0" }, "config": { diff --git a/composer.lock b/composer.lock index d220368..d8c75dc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "23d2c64b5bd955feec6a3a3d7dd6a841", + "content-hash": "0428f7bdfb99fd81d09175f70a82db43", "packages": [ { "name": "composer/semver", @@ -7314,6 +7314,137 @@ ], "time": "2024-05-11T07:35:57+00:00" }, + { + "name": "twig/intl-extra", + "version": "v3.10.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/intl-extra.git", + "reference": "693f6beb8ca91fc6323e01b3addf983812f65c93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/693f6beb8ca91fc6323e01b3addf983812f65c93", + "reference": "693f6beb8ca91fc6323e01b3addf983812f65c93", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/intl": "^5.4|^6.4|^7.0", + "twig/twig": "^3.10" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\Extra\\Intl\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension for Intl", + "homepage": "https://twig.symfony.com", + "keywords": [ + "intl", + "twig" + ], + "support": { + "source": "https://github.com/twigphp/intl-extra/tree/v3.10.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2024-05-11T07:35:57+00:00" + }, + { + "name": "twig/string-extra", + "version": "v3.10.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/string-extra.git", + "reference": "cd76ed8ae081bcd4fddf549e92e20c5df76c358a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/string-extra/zipball/cd76ed8ae081bcd4fddf549e92e20c5df76c358a", + "reference": "cd76ed8ae081bcd4fddf549e92e20c5df76c358a", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/string": "^5.4|^6.4|^7.0", + "symfony/translation-contracts": "^1.1|^2|^3", + "twig/twig": "^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\Extra\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension for Symfony String", + "homepage": "https://twig.symfony.com", + "keywords": [ + "html", + "string", + "twig", + "unicode" + ], + "support": { + "source": "https://github.com/twigphp/string-extra/tree/v3.10.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2024-05-11T07:35:57+00:00" + }, { "name": "twig/twig", "version": "v3.10.3", diff --git a/config/routes.yaml b/config/routes.yaml index 41ef814..3f8c5a6 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -3,3 +3,6 @@ controllers: path: ../src/Controller/ namespace: App\Controller type: attribute +profile_follow: + path: /profil/{id}/follow + controller: App\Controller\ProfileController::follow diff --git a/config/services.yaml b/config/services.yaml index 09f3146..0ffe7d2 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -25,3 +25,7 @@ services: # add more service definitions when explicit configuration is needed # please note that last definitions always *replace* previous ones + + App\Controller\ProfilController: + arguments: + $mgr: '@doctrine.orm.entity_manager' \ No newline at end of file diff --git a/dbapplication.session.sql b/dbapplication.session.sql new file mode 100644 index 0000000..e69de29 diff --git a/migrations/Version20240611131531.php b/migrations/Version20240611131531.php deleted file mode 100644 index 838e4bf..0000000 --- a/migrations/Version20240611131531.php +++ /dev/null @@ -1,41 +0,0 @@ -addSql('CREATE TEMPORARY TABLE __temp__post AS SELECT id, profil_id, title, text, is_dream, up_vote, down_vote FROM post'); - $this->addSql('DROP TABLE post'); - $this->addSql('CREATE TABLE post (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, profil_id INTEGER NOT NULL, title VARCHAR(255) DEFAULT NULL, text VARCHAR(512) DEFAULT NULL, is_dream BOOLEAN NOT NULL, up_vote INTEGER DEFAULT 0 NOT NULL, down_vote INTEGER DEFAULT 0 NOT NULL, CONSTRAINT FK_5A8A6C8D275ED078 FOREIGN KEY (profil_id) REFERENCES profil (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)'); - $this->addSql('INSERT INTO post (id, profil_id, title, text, is_dream, up_vote, down_vote) SELECT id, profil_id, title, text, is_dream, up_vote, down_vote FROM __temp__post'); - $this->addSql('DROP TABLE __temp__post'); - $this->addSql('CREATE INDEX IDX_5A8A6C8D275ED078 ON post (profil_id)'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE TEMPORARY TABLE __temp__post AS SELECT id, profil_id, title, text, is_dream, up_vote, down_vote FROM post'); - $this->addSql('DROP TABLE post'); - $this->addSql('CREATE TABLE post (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, profil_id INTEGER NOT NULL, title VARCHAR(255) DEFAULT NULL, text VARCHAR(512) DEFAULT NULL, is_dream BOOLEAN NOT NULL, up_vote INTEGER NOT NULL, down_vote INTEGER NOT NULL, CONSTRAINT FK_5A8A6C8D275ED078 FOREIGN KEY (profil_id) REFERENCES profil (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); - $this->addSql('INSERT INTO post (id, profil_id, title, text, is_dream, up_vote, down_vote) SELECT id, profil_id, title, text, is_dream, up_vote, down_vote FROM __temp__post'); - $this->addSql('DROP TABLE __temp__post'); - $this->addSql('CREATE INDEX IDX_5A8A6C8D275ED078 ON post (profil_id)'); - } -} diff --git a/migrations/Version20240612112105.php b/migrations/Version20240612112105.php deleted file mode 100644 index 451d975..0000000 --- a/migrations/Version20240612112105.php +++ /dev/null @@ -1,35 +0,0 @@ -addSql('ALTER TABLE profil ADD COLUMN roles CLOB DEFAULT NULL'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE TEMPORARY TABLE __temp__profil AS SELECT id, name, description, password FROM profil'); - $this->addSql('DROP TABLE profil'); - $this->addSql('CREATE TABLE profil (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) DEFAULT NULL, description VARCHAR(255) DEFAULT NULL, password VARCHAR(255) DEFAULT NULL)'); - $this->addSql('INSERT INTO profil (id, name, description, password) SELECT id, name, description, password FROM __temp__profil'); - $this->addSql('DROP TABLE __temp__profil'); - } -} diff --git a/migrations/Version20240612121601.php b/migrations/Version20240612121601.php deleted file mode 100644 index f40fbff..0000000 --- a/migrations/Version20240612121601.php +++ /dev/null @@ -1,35 +0,0 @@ -addSql('ALTER TABLE profil ADD COLUMN roles CLOB DEFAULT NULL'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE TEMPORARY TABLE __temp__profil AS SELECT id, name, description, password FROM profil'); - $this->addSql('DROP TABLE profil'); - $this->addSql('CREATE TABLE profil (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) DEFAULT NULL, description VARCHAR(255) DEFAULT NULL, password VARCHAR(255) DEFAULT NULL)'); - $this->addSql('INSERT INTO profil (id, name, description, password) SELECT id, name, description, password FROM __temp__profil'); - $this->addSql('DROP TABLE __temp__profil'); - } -} diff --git a/migrations/Version20240605095530.php b/migrations/Version20240612171415.php similarity index 89% rename from migrations/Version20240605095530.php rename to migrations/Version20240612171415.php index 94173ef..0fd4508 100644 --- a/migrations/Version20240605095530.php +++ b/migrations/Version20240612171415.php @@ -10,7 +10,7 @@ use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ -final class Version20240605095530 extends AbstractMigration +final class Version20240612171415 extends AbstractMigration { public function getDescription(): string { @@ -23,12 +23,13 @@ final class Version20240605095530 extends AbstractMigration $this->addSql('CREATE TABLE commentary (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, post_id INTEGER NOT NULL, profil_id INTEGER NOT NULL, text VARCHAR(255) DEFAULT NULL, CONSTRAINT FK_1CAC12CA4B89032C FOREIGN KEY (post_id) REFERENCES post (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1CAC12CA275ED078 FOREIGN KEY (profil_id) REFERENCES profil (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); $this->addSql('CREATE INDEX IDX_1CAC12CA4B89032C ON commentary (post_id)'); $this->addSql('CREATE INDEX IDX_1CAC12CA275ED078 ON commentary (profil_id)'); - $this->addSql('CREATE TABLE post (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, profil_id INTEGER NOT NULL, title VARCHAR(255) DEFAULT NULL, text VARCHAR(512) DEFAULT NULL, is_dream BOOLEAN NOT NULL, up_vote INTEGER NOT NULL, down_vote INTEGER NOT NULL, CONSTRAINT FK_5A8A6C8D275ED078 FOREIGN KEY (profil_id) REFERENCES profil (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); + $this->addSql('CREATE TABLE post (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, profil_id INTEGER NOT NULL, title VARCHAR(255) DEFAULT NULL, text VARCHAR(512) DEFAULT NULL, is_dream BOOLEAN NOT NULL, up_vote INTEGER DEFAULT 0 NOT NULL, down_vote INTEGER DEFAULT 0 NOT NULL, CONSTRAINT FK_5A8A6C8D275ED078 FOREIGN KEY (profil_id) REFERENCES profil (id) NOT DEFERRABLE INITIALLY IMMEDIATE)'); $this->addSql('CREATE INDEX IDX_5A8A6C8D275ED078 ON post (profil_id)'); $this->addSql('CREATE TABLE post_tags (post_id INTEGER NOT NULL, tags_id INTEGER NOT NULL, PRIMARY KEY(post_id, tags_id), CONSTRAINT FK_A6E9F32D4B89032C FOREIGN KEY (post_id) REFERENCES post (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_A6E9F32D8D7B4FB4 FOREIGN KEY (tags_id) REFERENCES tags (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); $this->addSql('CREATE INDEX IDX_A6E9F32D4B89032C ON post_tags (post_id)'); $this->addSql('CREATE INDEX IDX_A6E9F32D8D7B4FB4 ON post_tags (tags_id)'); - $this->addSql('CREATE TABLE profil (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) DEFAULT NULL, description VARCHAR(255) DEFAULT NULL, password VARCHAR(255) DEFAULT NULL)'); + $this->addSql('CREATE TABLE profil (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, roles CLOB DEFAULT NULL --(DC2Type:json) + , name VARCHAR(255) DEFAULT NULL, description VARCHAR(255) DEFAULT NULL, password VARCHAR(255) DEFAULT NULL)'); $this->addSql('CREATE TABLE profil_profil (profil_source INTEGER NOT NULL, profil_target INTEGER NOT NULL, PRIMARY KEY(profil_source, profil_target), CONSTRAINT FK_97293BC52E75F621 FOREIGN KEY (profil_source) REFERENCES profil (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_97293BC53790A6AE FOREIGN KEY (profil_target) REFERENCES profil (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)'); $this->addSql('CREATE INDEX IDX_97293BC52E75F621 ON profil_profil (profil_source)'); $this->addSql('CREATE INDEX IDX_97293BC53790A6AE ON profil_profil (profil_target)'); diff --git a/public/css/components/post_all.css b/public/css/components/post_all.css new file mode 100644 index 0000000..a2cdfa2 --- /dev/null +++ b/public/css/components/post_all.css @@ -0,0 +1,34 @@ +:root { + font-family: "Helvetica", 'Courier New', Courier, monospace; +} + +#wrapper { + display: flex; + flex-direction: column; + gap: 1rem; + margin: 2em auto; + width: 70%; + + >h1 { + color: white; + font-size: 3em; + margin-top: 0; + } +} + +html { + --s: 257px; + /* control the size */ + --c1: #38476b; + --c2: #bda3b6; + + --_c: var(--c1) calc(100% - var(--s)/2) 99%, #0000; + --_g: var(--s), #0000 calc(99% - var(--s)/2), var(--_c); + background: + radial-gradient(var(--s) at 100% var(--_g)), + radial-gradient(calc(var(--s)/4) at 50% calc(100%/3), var(--_c)) var(--s) 0, + radial-gradient(var(--s) at 0% var(--_g)) 0 calc(3*var(--s)) var(--c2); + background-size: + calc(2*var(--s)) calc(9*var(--s)/4), + calc(2*var(--s)) calc(3*var(--s)/4); +} \ No newline at end of file diff --git a/public/css/components/post_mini.css b/public/css/components/post_mini.css new file mode 100644 index 0000000..0ed7a8e --- /dev/null +++ b/public/css/components/post_mini.css @@ -0,0 +1,24 @@ +#post-wrapper { + background-color: #f2f2f7; + border: 3px solid black; + border-radius: 1rem; + padding: 20px; +} + +#post-info { + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; +} + +#post { + h1 { + font-size: 1.5em; + } + + p { + white-space: pre-wrap; + font-size: 1em; + } +} diff --git a/public/images/userFiller.png b/public/images/userFiller.png new file mode 100644 index 0000000..7b5068c Binary files /dev/null and b/public/images/userFiller.png differ diff --git a/src/Controller/PostController.php b/src/Controller/PostController.php index 50f58ab..73a0396 100644 --- a/src/Controller/PostController.php +++ b/src/Controller/PostController.php @@ -55,12 +55,12 @@ class PostController extends AbstractController $this->denyAccessUnlessGranted('IS_AUTHENTICATED'); $post = new Post(); - $form = $this->createForm(PostType::class, $post); - $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { + //$dateNow = new DateTime(); + $form = $form->getData(); $user = $this->getUser(); $post->setProfil($user); diff --git a/src/Controller/ProfilController.php b/src/Controller/ProfilController.php index 34d6be1..0540944 100644 --- a/src/Controller/ProfilController.php +++ b/src/Controller/ProfilController.php @@ -2,17 +2,146 @@ namespace App\Controller; +use App\Entity\Profil; +use App\Form\ProfilType; +use Doctrine\ORM\EntityManager; +use SebastianBergmann\Environment\Console; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\HttpFoundation\Request; class ProfilController extends AbstractController { - #[Route('/profil', name: 'app_profil')] - public function index(): Response + + + public function __construct(private EntityManager $mgr) + { + } + #[Route(path:"/profil", name:"profil_perso", methods: ["GET"])] + public function baseProfil(): Response + { + $this->denyAccessUnlessGranted('IS_AUTHENTICATED'); + return $this->redirectToRoute('profil_show', ['id' => $this->getUser()->getId()]); + } + #[Route('/profil/{id}',name:'profil_show', requirements: ['page' => '\d+'])] + public function profil(int $id): Response { + $connected = $this->isGranted('ROLE_USER'); + // $connected = $this->isGranted('ROLE_USER') != false; + + $profil = $this->mgr->find(Profil::class, $id); + $posts = $profil->getPosts(); return $this->render('profil/index.html.twig', [ - 'controller_name' => 'ProfilController', + 'profil' => $profil, + 'posts' => $posts, + 'followFlag' => $profil->getFollowers()->contains($this->getUser()), + 'selfFlag' => $profil == $this->getUser(), + 'connected' => $connected ]); } + + #[Route('/profil/{id}/follow',name:'profil_follow', requirements: ['page' => '\d+'])] + public function followProfil(int $id): Response + { + $profil = $this->mgr->find(Profil::class, $id); + if ($profil instanceof Profil) { + $profil->addFollower($this->getUser()); + $this->mgr->persist($profil); + $this->mgr->flush(); + $this->addFlash('success',''); + return $this->redirectToRoute('profil_show', ['id' => $id]); + } else { + $this->addFlash('error',''); + return $this->render('error.html.twig', []); + } + } + + #[Route('/profil/{id}/unfollow',name:'profil_unfollow', requirements: ['page' => '\d+'])] + public function unfollowProfil(int $id): Response + { + $profil = $this->mgr->find(Profil::class, $id); + if ($profil instanceof Profil) { + $profil->removeFollower($this->getUser()); + $this->mgr->persist($profil); + $this->mgr->flush(); + $this->addFlash('success',''); + return $this->redirectToRoute('profil_show', ['id' => $id]); + } else { + $this->addFlash('error',''); + return $this->render('error.html.twig', []); + } + } + + #[Route('/profil/{id}/followers',name:'profil_followers', requirements: ['page'=> '\d'])] + public function getFollowers(int $id): Response + { + $profil = $this->mgr->find(Profil::class, $id); + + return $this->render('profil/follow-list.html.twig', [ + 'title'=> 'Followers', + 'profils' => $profil->getFollowers() + ]); + } + + #[Route('/profil/{id}/following',name:'profil_following', requirements: ['page'=> '\d'])] + public function getFollowing(int $id): Response + { + $profil = $this->mgr->find(Profil::class, $id); + + return $this->render('profil/follow-list.html.twig', [ + 'title'=> 'Following', + 'profils' => $profil->getFollowing() + ]); + } + + // #[Route('/profil/new', name: 'profil_new')] + // public function new(): Response + // { + // $profil = new Profil(); + + // return $this->redirectToRoute('profil_show', ['id' => $profil->getId()]); + // } + + #[Route('/profil/{id}/edit',name:'profil_edit', requirements: ['page'=> '\d'])] + public function editProfil(int $id,Request $request): Response + { + $profil = $this->mgr->find(Profil::class, $id); + + $form = $this->createForm(ProfilType::class, $profil); + + $form->handleRequest($request); + if( $form->isSubmitted() && $form->isValid() ) { + $form->getData(); + $this->mgr->persist($profil); + $this->mgr->flush(); + + return $this->redirectToRoute('profil_show', ['id' => $id]); + } + + return $this->render('profil/edit.html.twig', [ + 'form'=> $form, + 'profil' => $profil, + ]); + } + + #[Route('/profil/{id}/delete', name: 'profil_delete', methods: ['POST'], requirements: ['id' => '\d+'])] + public function delete(int $id, Request $request): Response + { + $profil = $this->mgr->find(Profil::class, $id); + + if (!$profil) { + throw $this->createNotFoundException('The profile does not exist'); + } + + if ($this->isCsrfTokenValid('delete'.$profil->getId(), $request->request->get('_token'))) { + $this->mgr->remove($profil); + $this->mgr->flush(); + $this->addFlash('success', 'Profile deleted successfully'); + } + + return $this->redirectToRoute('app_login'); + } + + } diff --git a/src/Entity/Post.php b/src/Entity/Post.php index 7d7a2db..26143f4 100644 --- a/src/Entity/Post.php +++ b/src/Entity/Post.php @@ -3,6 +3,7 @@ namespace App\Entity; use App\Repository\PostRepository; +use DateTime; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; @@ -24,6 +25,9 @@ class Post #[ORM\Column] private ?bool $isDream = null; + // #[ORM\Column()] + // private ?DateTime $dateCreated = null; + #[ORM\Column(options: ["default" => 0])] private int $upVote = 0; @@ -93,6 +97,18 @@ class Post return $this; } + public function getDateCreated(): ?DateTime + { + return $this->dateCreated; + } + + public function setDateCreated(?DateTime $dateCreated): static + { + $this->dateCreated = $dateCreated; + + return $this; + } + public function getUpVote(): ?int { return $this->upVote; diff --git a/src/Entity/Profil.php b/src/Entity/Profil.php index 571fba6..0909a5c 100644 --- a/src/Entity/Profil.php +++ b/src/Entity/Profil.php @@ -46,14 +46,23 @@ class Profil implements UserInterface, PasswordAuthenticatedUserInterface /** * @var Collection */ - #[ORM\ManyToMany(targetEntity: self::class, inversedBy: 'followers')] + #[ORM\ManyToMany(targetEntity: self::class, inversedBy: 'following')] private Collection $followers; + /** + * @var Collection + */ + #[ORM\ManyToMany(targetEntity: self::class, mappedBy: 'followers')] + private Collection $following; + + public function __construct() { $this->posts = new ArrayCollection(); $this->commentaries = new ArrayCollection(); $this->followers = new ArrayCollection(); + $this->following = new ArrayCollection(); + } public function getId(): ?int @@ -162,6 +171,33 @@ class Profil implements UserInterface, PasswordAuthenticatedUserInterface return $this; } + + + public function getRoles(): array + { + $roles = $this->roles; + // guarantee every user at least has ROLE_USER + // $roles[] = 'ROLE_USER'; + + return array_unique($roles); + } + + public function setRoles(array $roles): self + { + $this->roles = $roles; + return $this; + } + + public function eraseCredentials(): void + { + // TODO: Implement eraseCredentials() method. + } + + public function getUserIdentifier(): string + { + return $this->name; + } + /** * @return Collection */ @@ -172,7 +208,7 @@ class Profil implements UserInterface, PasswordAuthenticatedUserInterface public function addFollower(self $follower): static { - if (!$this->followers->contains($follower)) { + if (!$this->followers->contains($follower) && $follower!=$this) { $this->followers->add($follower); } @@ -186,28 +222,30 @@ class Profil implements UserInterface, PasswordAuthenticatedUserInterface return $this; } - public function getRoles(): array + /** + * @return Collection + */ + public function getFollowing(): Collection { - $roles = $this->roles; - // guarantee every user at least has ROLE_USER - // $roles[] = 'ROLE_USER'; - - return array_unique($roles); + return $this->following; } - public function setRoles(array $roles): self + public function addFollowing(self $following): static { - $this->roles = $roles; + if (!$this->following->contains($following) && $following!=$this) { + $this->following->add($following); + $following->addFollower($this); + } + return $this; } - public function getUserIdentifier(): string + public function removeFollowing(self $following): static { - return $this->name; - } + if ($this->following->removeElement($following)) { + $following->removeFollower($this); + } - public function eraseCredentials(): void - { - // TODO: Implement eraseCredentials() method. + return $this; } } diff --git a/src/Form/ProfilType.php b/src/Form/ProfilType.php new file mode 100644 index 0000000..d6d5f2e --- /dev/null +++ b/src/Form/ProfilType.php @@ -0,0 +1,29 @@ +add('name') + ->add('description') + // ->add('password') + + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => Profil::class, + ]); + } +} diff --git a/templates/error.html.twig b/templates/error.html.twig new file mode 100644 index 0000000..3cd56e1 --- /dev/null +++ b/templates/error.html.twig @@ -0,0 +1,56 @@ + + + + + + Error + + + +
+

Something went wrong

+

We're sorry, but something went wrong. Please try again later.

+ {# Go to Homepage #} +
+ + diff --git a/templates/post/all.html.twig b/templates/post/all.html.twig index 1aa738b..159947c 100644 --- a/templates/post/all.html.twig +++ b/templates/post/all.html.twig @@ -1,9 +1,11 @@ -

All website posts

-Créer un nouveau post -{% for post in posts %} -
- Id: {{ post.id }} - Title: {{ post.title }} - Content: {{ post.text }} -
-{% endfor %} + + + + +
+

All posts

+ + {% for post in posts %} + {% include 'post/post_mini.html.twig' with {'post' : post} %} + {% endfor %} +
diff --git a/templates/post/post_mini.html.twig b/templates/post/post_mini.html.twig new file mode 100644 index 0000000..0081a38 --- /dev/null +++ b/templates/post/post_mini.html.twig @@ -0,0 +1,13 @@ +
+
+
+ + {{ post.profil.name }} + {# - {{ post.dateCreated }} #} + - Il y a 3 jours +
+ +

{{ post.title }}

+

{{ post.text|u.truncate(150, true, '...') }}

+
+
diff --git a/templates/profil/edit.html.twig b/templates/profil/edit.html.twig new file mode 100644 index 0000000..6806fec --- /dev/null +++ b/templates/profil/edit.html.twig @@ -0,0 +1,30 @@ +{# templates/profil/edit.html.twig #} + +{% extends 'base.html.twig' %} + +{% block title %}Edit Profile{% endblock %} + +{% block stylesheets %} + + +{% endblock %} + +{% block body %} +
+

Edit Profile

+ + {{ form_start(form) }} + {{ form_widget(form) }} + + {{ form_end(form) }} + +
+ + +
+
+{% endblock %} + +{% block javascripts %} + +{% endblock %} \ No newline at end of file diff --git a/templates/profil/follow-list.html.twig b/templates/profil/follow-list.html.twig new file mode 100644 index 0000000..4ade7db --- /dev/null +++ b/templates/profil/follow-list.html.twig @@ -0,0 +1,55 @@ +{# templates/profil/list.html.twig #} + +{% extends 'base.html.twig' %} + +{% block title %}Liste des Profils{% endblock %} + +{% block stylesheets %} + + + +{% endblock %} + +{% block body %} + +

{{title }} :

+
+ {% for profil in profils %} + + {% endfor %} +
+{% endblock %} + +{% block javascripts %} + +{% endblock %} diff --git a/templates/profil/index.html.twig b/templates/profil/index.html.twig index e2c425e..a380683 100644 --- a/templates/profil/index.html.twig +++ b/templates/profil/index.html.twig @@ -1,20 +1,55 @@ +{# templates/profil/show.html.twig #} + {% extends 'base.html.twig' %} -{% block title %}Hello ProfilController!{% endblock %} +{% block title %}Profil - {{ profil.name }}{% endblock %} + +{% block stylesheets %} + + +{% endblock %} {% block body %} - +
+
+ Profile Image +
+

{{ profil.name }}

+
+

{{ profil.description }}

+ +
+
+
+ + {% if selfFlag %} + + {% elseif connected %} + {% if followFlag %} + + {% else %} + + {% endif %} + {% endif %} + {# #} + +
+
-
-

Hello {{ controller_name }}! ✅

+
+ {% if posts|length > 0 %} +
    + {% for post in posts %} + {% include 'post/post_mini.html.twig' with {'post': post} %} + {% endfor %} +
+ {% endif %} +
+{% endblock %} - This friendly message is coming from: -
    -
  • Your controller at /home/aurian/3eme_annee/assassymfony/fukafukashita/src/Controller/ProfilController.php
  • -
  • Your template at /home/aurian/3eme_annee/assassymfony/fukafukashita/templates/profil/index.html.twig
  • -
-
+{% block javascripts %} + {% endblock %} diff --git a/var/data.db b/var/data.db index 7f6fd33..3346c70 100644 Binary files a/var/data.db and b/var/data.db differ