'
10 | email: 'john@gmail.com'
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "symfony-blog",
3 | "version": "1.0.0",
4 | "description": "symfony-blog",
5 | "main": "app.js",
6 | "dependencies": {
7 | "bulma": "^0.7.1",
8 | "font-awesome": "^4.7.0",
9 | "jquery": "^3.3.1"
10 | },
11 | "devDependencies": {
12 | "@symfony/webpack-encore": "^0.22.0",
13 | "node-sass": "^4.10.0",
14 | "sass-loader": "^7.1.0",
15 | "webpack-notifier": "^1.6.0"
16 | },
17 | "license": "MIT",
18 | "private": true,
19 | "scripts": {
20 | "dev-server": "encore dev-server",
21 | "dev": "encore dev",
22 | "watch": "encore dev --watch",
23 | "build": "encore production --progress"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | tests
21 |
22 |
23 |
24 |
25 |
26 | src
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pifaace/symfony-blog/2c28217e824a8e048750ff87f598e883e225385a/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | handle($request);
25 | $response->send();
26 | $kernel->terminate($request, $response);
27 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # www.robotstxt.org/
2 | # www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449
3 |
4 | User-agent: *
5 | Disallow:
6 |
--------------------------------------------------------------------------------
/src/Controller/Admin/AdminController.php:
--------------------------------------------------------------------------------
1 | render('backoffice/dashboard/dashboard.html.twig', [
20 | 'countArticles' => $article->countArticles(),
21 | 'countComments' => $comment->countComments(),
22 | 'countUsers' => $user->countUsers(),
23 | ]);
24 | }
25 |
26 | /**
27 | * @Route("/admin/articles", name="admin-articles", methods={"GET"})
28 | */
29 | public function listArticle(ArticleRepository $articleRepository): Response
30 | {
31 | return $this->render('backoffice/article/list.html.twig', [
32 | 'articles' => $articleRepository->getArticlesWithComment(),
33 | ]);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Controller/Admin/ArticleController.php:
--------------------------------------------------------------------------------
1 | articleManager = $articleManager;
32 | $this->trans = $trans;
33 | }
34 |
35 | /**
36 | * @Route("admin/article/new", name="article_new", methods={"GET", "POST"})
37 | */
38 | public function new(Request $request, FlashMessage $flashMessage, Notifier $notifier, Publisher $publisher): Response
39 | {
40 | $article = new Article();
41 | $form = $this->createForm(ArticleType::class, $article);
42 |
43 | $form->handleRequest($request);
44 |
45 | if ($form->isSubmitted() && $form->isValid()) {
46 | $this->articleManager->create($article);
47 |
48 | $notifier->articleCreated($article, $publisher);
49 |
50 | $flashMessage->createMessage(
51 | $request,
52 | FlashMessage::INFO_MESSAGE,
53 | $this->trans->trans('backoffice.articles.flashmessage_publish'));
54 |
55 | return $this->redirectToRoute('admin-articles');
56 | }
57 |
58 | return $this->render('backoffice/article/add.html.twig', [
59 | 'form' => $form->createView(),
60 | ]);
61 | }
62 |
63 | /**
64 | * @Route("admin/article/{slug}/edit", name="article_edit", methods={"GET", "POST"})
65 | */
66 | public function edit(Request $request, Article $article, FlashMessage $flashMessage): Response
67 | {
68 | $image = $article->getImage();
69 | $form = $this->createForm(ArticleType::class, $article);
70 |
71 | $form->handleRequest($request);
72 | if ($form->isSubmitted() && $form->isValid()) {
73 | $this->articleManager->edit($article);
74 |
75 | $flashMessage->createMessage(
76 | $request,
77 | FlashMessage::INFO_MESSAGE,
78 | $this->trans->trans('backoffice.articles.flashmessage_edit')
79 | );
80 |
81 | return $this->redirectToRoute('article_edit', ['slug' => $article->getSlug()]);
82 | }
83 |
84 | return $this->render('backoffice/article/edit.html.twig', [
85 | 'article' => $article,
86 | 'form' => $form->createView(),
87 | 'currentImage' => $image,
88 | ]);
89 | }
90 |
91 | /**
92 | * @Route("admin/article/{slug}/delete", name="article_delete", methods={"GET", "POST"})
93 | */
94 | public function delete(Request $request, Article $article, FlashMessage $flashMessage): Response
95 | {
96 | $this->articleManager->remove($article);
97 |
98 | $flashMessage->createMessage(
99 | $request,
100 | FlashMessage::INFO_MESSAGE,
101 | $this->trans->trans('backoffice.articles.flashmessage_deleted_article')
102 | );
103 |
104 | return $this->redirectToRoute('admin-articles');
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/Controller/BlogController.php:
--------------------------------------------------------------------------------
1 | getPage();
26 | $articles = $paginator->getItemList($articleRepository, $page);
27 | $nbPages = $paginator->countPage($articles);
28 |
29 | return $this->render('blog/home/index.html.twig', [
30 | 'articles' => $articles,
31 | 'nbPages' => $nbPages,
32 | 'page' => $page,
33 | ]);
34 | }
35 |
36 | /**
37 | * @Route("article/{slug}", name="article_show", methods={"GET", "POST"})
38 | */
39 | public function show(Article $article): Response
40 | {
41 | return $this->render('blog/article/show.html.twig', [
42 | 'article' => $article,
43 | ]);
44 | }
45 |
46 | /**
47 | * @Route("article/{slug}/comment/new", name="comment_new", methods={"POST"})
48 | */
49 | public function newComment(Request $request, Article $article): Response
50 | {
51 | $comment = new Comment();
52 | $form = $this->createForm(CommentType::class, $comment);
53 |
54 | $form->handleRequest($request);
55 | if ($form->isSubmitted() && $form->isValid()) {
56 | $em = $this->getDoctrine()->getManager();
57 | $user = $this->getUser();
58 | $article->addComment($comment);
59 | $user->addComment($comment);
60 |
61 | $em->persist($comment);
62 | $em->flush();
63 | }
64 |
65 | return $this->redirectToRoute('article_show', ['slug' => $article->getSlug()]);
66 | }
67 |
68 | /**
69 | * @ParamConverter()
70 | */
71 | public function commentForm(Article $article): Response
72 | {
73 | $form = $this->createForm(CommentType::class);
74 |
75 | return $this->render('blog/article/_comment_form.html.twig', [
76 | 'form' => $form->createView(),
77 | 'article' => $article,
78 | ]);
79 | }
80 |
81 | /**
82 | * @Route("/switch-locale/{locale}", name="switch_locale", methods={"GET"})
83 | */
84 | public function switchLocale(Request $request, string $locale)
85 | {
86 | $request->getSession()->set('locale', $locale);
87 |
88 | return $this->redirectToRoute('homepage');
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/Controller/NotificationController.php:
--------------------------------------------------------------------------------
1 | isXmlHttpRequest()) {
25 | $unreadNotifications = $userNotificationRepository->findBy(['user' => $user, 'isRead' => false]);
26 |
27 | if (!empty($unreadNotifications)) {
28 | foreach ($unreadNotifications as $userNotification) {
29 | $userNotification->setIsRead(true);
30 | }
31 |
32 | $em->flush();
33 |
34 | return new Response('', 204);
35 | }
36 | }
37 |
38 | return new Response('Invalid request');
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Controller/UserSettingsController.php:
--------------------------------------------------------------------------------
1 | render('blog/user/profile/show.html.twig', [
20 | 'user' => $this->getUser(),
21 | ]);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Entity/Comment.php:
--------------------------------------------------------------------------------
1 | id;
58 | }
59 |
60 | public function setContent(string $content): self
61 | {
62 | $this->content = $content;
63 |
64 | return $this;
65 | }
66 |
67 | public function getContent(): ?string
68 | {
69 | return $this->content;
70 | }
71 |
72 | /**
73 | * @ORM\PrePersist()
74 | */
75 | public function setCreateAt()
76 | {
77 | $this->createAt = new \DateTime();
78 | }
79 |
80 | public function getCreateAt(): \DateTime
81 | {
82 | return $this->createAt;
83 | }
84 |
85 | public function setArticle(Article $article): self
86 | {
87 | $this->article = $article;
88 |
89 | return $this;
90 | }
91 |
92 | public function getArticle(): Article
93 | {
94 | return $this->article;
95 | }
96 |
97 | public function getUser(): ?User
98 | {
99 | return $this->user;
100 | }
101 |
102 | public function setUser(User $user): void
103 | {
104 | $this->user = $user;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/Entity/Image.php:
--------------------------------------------------------------------------------
1 | id;
49 | }
50 |
51 | public function getFile(): ?UploadedFile
52 | {
53 | return $this->file;
54 | }
55 |
56 | public function setFile(UploadedFile $file = null): self
57 | {
58 | $this->file = $file;
59 |
60 | return $this;
61 | }
62 |
63 | public function setAlt(string $alt): self
64 | {
65 | $this->alt = $alt;
66 |
67 | return $this;
68 | }
69 |
70 | public function getAlt(): string
71 | {
72 | return $this->alt;
73 | }
74 |
75 | /**
76 | * @return bool
77 | */
78 | public function isDeletedImage(): ?bool
79 | {
80 | return $this->deletedImage;
81 | }
82 |
83 | public function setDeletedImage(bool $deletedImage): void
84 | {
85 | $this->deletedImage = $deletedImage;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/Entity/Notification.php:
--------------------------------------------------------------------------------
1 | userNotifications = new ArrayCollection();
56 | }
57 |
58 | public function getId(): int
59 | {
60 | return $this->id;
61 | }
62 |
63 | public function getCreatedAt(): ?\DateTimeInterface
64 | {
65 | return $this->createdAt;
66 | }
67 |
68 | /**
69 | * @ORM\PrePersist
70 | */
71 | public function setCreatedAt(): self
72 | {
73 | $this->createdAt = new \DateTime();
74 |
75 | return $this;
76 | }
77 |
78 | public function getNotificationType(): NotificationType
79 | {
80 | return $this->notificationType;
81 | }
82 |
83 | public function setNotificationType(NotificationType $notificationType): self
84 | {
85 | $this->notificationType = $notificationType;
86 |
87 | return $this;
88 | }
89 |
90 | public function getCreatedBy(): User
91 | {
92 | return $this->createdBy;
93 | }
94 |
95 | public function setCreatedBy(UserInterface $createdBy): self
96 | {
97 | $this->createdBy = $createdBy;
98 |
99 | return $this;
100 | }
101 |
102 | public function getTargetLink(): string
103 | {
104 | return $this->targetLink;
105 | }
106 |
107 | public function setTargetLink(string $targetLink): self
108 | {
109 | $this->targetLink = $targetLink;
110 |
111 | return $this;
112 | }
113 |
114 | public function addUserNotification(UserNotification $userNotification): void
115 | {
116 | $userNotification->setNotification($this);
117 | if (!$this->userNotifications->contains($userNotification)) {
118 | $this->userNotifications->add($userNotification);
119 | }
120 | }
121 |
122 | public function removeUserNotification(UserNotification $userNotification): void
123 | {
124 | $this->userNotifications->removeElement($userNotification);
125 | }
126 |
127 | public function getComments(): ?Collection
128 | {
129 | return $this->userNotifications;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/Entity/NotificationType.php:
--------------------------------------------------------------------------------
1 | id;
29 | }
30 |
31 | public function getName(): ?string
32 | {
33 | return $this->name;
34 | }
35 |
36 | public function setName(string $name): self
37 | {
38 | $this->name = $name;
39 |
40 | return $this;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Entity/Tag.php:
--------------------------------------------------------------------------------
1 | id;
32 | }
33 |
34 | public function setName(string $name): self
35 | {
36 | $this->name = $name;
37 |
38 | return $this;
39 | }
40 |
41 | public function getName(): string
42 | {
43 | return $this->name;
44 | }
45 |
46 | public function __toString(): string
47 | {
48 | return $this->getName();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Entity/UserNotification.php:
--------------------------------------------------------------------------------
1 | isRead = false;
45 | }
46 |
47 | public function getId(): ?int
48 | {
49 | return $this->id;
50 | }
51 |
52 | public function getIsRead(): ?bool
53 | {
54 | return $this->isRead;
55 | }
56 |
57 | public function setIsRead(bool $isRead): self
58 | {
59 | $this->isRead = $isRead;
60 |
61 | return $this;
62 | }
63 |
64 | public function getNotification(): Notification
65 | {
66 | return $this->notification;
67 | }
68 |
69 | public function setNotification(Notification $notification): void
70 | {
71 | $this->notification = $notification;
72 | }
73 |
74 | public function getUser(): User
75 | {
76 | return $this->user;
77 | }
78 |
79 | public function setUser(User $user): void
80 | {
81 | $this->user = $user;
82 | }
83 |
84 | public function getCreatedAt(): \DateTime
85 | {
86 | return $this->createdAt;
87 | }
88 |
89 | /**
90 | * @ORM\PrePersist
91 | */
92 | public function setCreatedAt(): void
93 | {
94 | $this->createdAt = new \DateTime();
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/EventSubscriber/PasswordTokenReset.php:
--------------------------------------------------------------------------------
1 | getSubject();
16 | if (!$user instanceof User && null === $user->getResetPasswordToken()) {
17 | return;
18 | }
19 | $user->setResetPasswordToken(null);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/EventSubscriber/PreferredLocaleSubscriber.php:
--------------------------------------------------------------------------------
1 | supportedLocales = $supportedLocales;
24 | $this->defaultLocale = $locale;
25 | }
26 |
27 | public static function getSubscribedEvents()
28 | {
29 | return [
30 | KernelEvents::REQUEST => [['onKernelRequest', 20]],
31 | ];
32 | }
33 |
34 | public function onKernelRequest(GetResponseEvent $event)
35 | {
36 | $request = $event->getRequest();
37 | $localeNeeded = $request->getSession()->get('locale');
38 |
39 | if (in_array($localeNeeded, $this->supportedLocales)) {
40 | $request->setLocale($localeNeeded);
41 | } else {
42 | $request->setLocale($this->defaultLocale);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/EventSubscriber/SetMercureCookieSubscriber.php:
--------------------------------------------------------------------------------
1 | cookieGenerator = $cookieGenerator;
26 | $this->tokenStorage = $tokenStorage;
27 | }
28 |
29 | public static function getSubscribedEvents()
30 | {
31 | return [
32 | 'kernel.response' => 'onKernelResponse',
33 | ];
34 | }
35 |
36 | public function onKernelResponse(ResponseEvent $event)
37 | {
38 | if (null === $this->tokenStorage->getToken() || !$this->tokenStorage->getToken()->getUser() instanceof User) {
39 | return;
40 | }
41 |
42 | $cookie = $this->cookieGenerator->generate();
43 |
44 | $event->getResponse()->headers->set(
45 | 'set-cookie',
46 | $cookie
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/EventSubscriber/UserActionSubscriber.php:
--------------------------------------------------------------------------------
1 | logger = $logger;
20 | }
21 |
22 | public function getSubscribedEvents(): array
23 | {
24 | return [
25 | Events::postPersist,
26 | Events::postUpdate,
27 | ];
28 | }
29 |
30 | public function postPersist(LifecycleEventArgs $args): void
31 | {
32 | $this->logger->userAction(\get_class($args->getObject()), 'created');
33 | }
34 |
35 | public function postUpdate(LifecycleEventArgs $args): void
36 | {
37 | $this->logger->userAction(\get_class($args->getObject()), 'updated');
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Events.php:
--------------------------------------------------------------------------------
1 | add('title', TextType::class, ['label' => false])
21 | ->add('content', TextareaType::class, ['label' => false])
22 | ->add('image', ImageType::class, [
23 | 'required' => false,
24 | 'label' => false,
25 | ])
26 | ->add('tags', TagsType::class, ['required' => false]);
27 | }
28 |
29 | /**
30 | * {@inheritdoc}
31 | */
32 | public function configureOptions(OptionsResolver $resolver)
33 | {
34 | $resolver->setDefaults([
35 | 'data_class' => 'App\Entity\Article',
36 | ]);
37 | }
38 |
39 | /**
40 | * {@inheritdoc}
41 | */
42 | public function getBlockPrefix()
43 | {
44 | return 'App_article';
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Form/CommentType.php:
--------------------------------------------------------------------------------
1 | add('content', TextareaType::class);
19 | }
20 |
21 | /**
22 | * {@inheritdoc}
23 | */
24 | public function configureOptions(OptionsResolver $resolver)
25 | {
26 | $resolver->setDefaults([
27 | 'data_class' => 'App\Entity\Comment',
28 | ]);
29 | }
30 |
31 | /**
32 | * {@inheritdoc}
33 | */
34 | public function getBlockPrefix()
35 | {
36 | return 'App_comment';
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Form/DataTransformer/TagsTransformer.php:
--------------------------------------------------------------------------------
1 | em = $em;
19 | }
20 |
21 | public function transform($value): string
22 | {
23 | return implode(', ', $value);
24 | }
25 |
26 | public function reverseTransform($string): array
27 | {
28 | $names = array_filter(array_unique(array_map('trim', explode(',', $string))));
29 |
30 | $tags = $this->em->getRepository('App:Tag')->findBy([
31 | 'name' => $names,
32 | ]);
33 |
34 | $newNames = array_diff($names, $tags);
35 |
36 | foreach ($newNames as $newName) {
37 | $tag = new Tag();
38 | $tag->setName($newName);
39 |
40 | $tags[] = $tag;
41 | }
42 |
43 | return $tags;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Form/ImageType.php:
--------------------------------------------------------------------------------
1 | add('file', FileType::class, [
21 | 'label' => false,
22 | 'required' => false,
23 | ]);
24 |
25 | $builder->addEventListener(
26 | FormEvents::PRE_SET_DATA,
27 | function (FormEvent $event) {
28 | $image = $event->getData();
29 |
30 | if (null == $image) {
31 | return;
32 | }
33 |
34 | if (null != $image->getId()) {
35 | $event->getForm()->add('deletedImage', CheckboxType::class, [
36 | 'required' => false,
37 | 'label' => false,
38 | 'attr' => [
39 | 'hidden' => true,
40 | 'class' => 'delete-img-confirm',
41 | ],
42 | ]);
43 | } else {
44 | $event->getForm()->remove('deletedImage');
45 | }
46 | }
47 | );
48 | }
49 |
50 | /**
51 | * {@inheritdoc}
52 | */
53 | public function configureOptions(OptionsResolver $resolver)
54 | {
55 | $resolver->setDefaults([
56 | 'data_class' => 'App\Entity\Image',
57 | ]);
58 | }
59 |
60 | /**
61 | * {@inheritdoc}
62 | */
63 | public function getBlockPrefix()
64 | {
65 | return 'App_image';
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Form/LoginType.php:
--------------------------------------------------------------------------------
1 | add('username', TextType::class, ['label' => false])
17 | ->add('password', PasswordType::class, ['label' => false]);
18 | }
19 |
20 | public function configureOptions(OptionsResolver $resolver)
21 | {
22 | $resolver->setDefaults([
23 | 'csrf_token_id' => 'login_authenticate',
24 | ]);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Form/PasswordResetNewType.php:
--------------------------------------------------------------------------------
1 | add('plainPassword', RepeatedType::class, [
18 | 'type' => PasswordType::class,
19 | 'invalid_message' => 'reset_password.same_password',
20 | ]);
21 | }
22 |
23 | public function configureOptions(OptionsResolver $resolver)
24 | {
25 | $resolver->setDefaults([
26 | 'data_class' => User::class,
27 | ]);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Form/PasswordResetRequestType.php:
--------------------------------------------------------------------------------
1 | add('email', EmailType::class, [
17 | 'constraints' => new NotBlank(['message' => 'forgot_password.email_required']),
18 | ])
19 | ;
20 | }
21 |
22 | public function configureOptions(OptionsResolver $resolver)
23 | {
24 | $resolver->setDefaults([
25 | // uncomment if you want to bind to a class
26 | //'data_class' => PasswordReset::class,
27 | ]);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Form/RegistrationType.php:
--------------------------------------------------------------------------------
1 | add('username', TextType::class)
20 | ->add('email', EmailType::class)
21 | ->add('plainPassword', RepeatedType::class, [
22 | 'type' => PasswordType::class,
23 | 'invalid_message' => 'signin.same_password',
24 | ])
25 | ;
26 | }
27 |
28 | public function configureOptions(OptionsResolver $resolver)
29 | {
30 | $resolver->setDefaults([
31 | 'data_class' => User::class,
32 | ]);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Form/Type/TagsType.php:
--------------------------------------------------------------------------------
1 | em = $em;
22 | }
23 |
24 | public function buildForm(FormBuilderInterface $builder, array $options)
25 | {
26 | $builder
27 | ->addModelTransformer(new CollectionToArrayTransformer(), true)
28 | ->addModelTransformer(new TagsTransformer($this->em), true);
29 | }
30 |
31 | public function getParent()
32 | {
33 | return HiddenType::class;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Kernel.php:
--------------------------------------------------------------------------------
1 | getProjectDir().'/var/cache/'.$this->environment;
20 | }
21 |
22 | public function getLogDir()
23 | {
24 | return $this->getProjectDir().'/var/log';
25 | }
26 |
27 | public function registerBundles()
28 | {
29 | $contents = require $this->getProjectDir().'/config/bundles.php';
30 | foreach ($contents as $class => $envs) {
31 | if (isset($envs['all']) || isset($envs[$this->environment])) {
32 | yield new $class();
33 | }
34 | }
35 | }
36 |
37 | protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader)
38 | {
39 | $container->setParameter('container.autowiring.strict_mode', true);
40 | $container->setParameter('container.dumper.inline_class_loader', true);
41 | $confDir = $this->getProjectDir().'/config';
42 | $loader->load($confDir.'/packages/*'.self::CONFIG_EXTS, 'glob');
43 | if (is_dir($confDir.'/packages/'.$this->environment)) {
44 | $loader->load($confDir.'/packages/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob');
45 | }
46 | $loader->load($confDir.'/services'.self::CONFIG_EXTS, 'glob');
47 | $loader->load($confDir.'/services_'.$this->environment.self::CONFIG_EXTS, 'glob');
48 | }
49 |
50 | protected function configureRoutes(RouteCollectionBuilder $routes)
51 | {
52 | $confDir = $this->getProjectDir().'/config';
53 | if (is_dir($confDir.'/routes/')) {
54 | $routes->import($confDir.'/routes/*'.self::CONFIG_EXTS, '/', 'glob');
55 | }
56 | if (is_dir($confDir.'/routes/'.$this->environment)) {
57 | $routes->import($confDir.'/routes/'.$this->environment.'/**/*'.self::CONFIG_EXTS, '/', 'glob');
58 | }
59 | $routes->import($confDir.'/routes'.self::CONFIG_EXTS, '/', 'glob');
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Migrations/Version20171224150255.php:
--------------------------------------------------------------------------------
1 | abortIf('mysql' !== $this->connection->getDatabasePlatform()->getName(), 'Migration can only be executed safely on \'mysql\'.');
19 |
20 | $this->addSql('CREATE TABLE tag (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
21 | $this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, username VARCHAR(25) NOT NULL, password VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, role LONGTEXT NOT NULL COMMENT \'(DC2Type:array)\', UNIQUE INDEX UNIQ_8D93D649F85E0677 (username), UNIQUE INDEX UNIQ_8D93D649E7927C74 (email), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
22 | $this->addSql('CREATE TABLE comment (id INT AUTO_INCREMENT NOT NULL, article_id INT NOT NULL, username VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, content VARCHAR(255) NOT NULL, createAt DATETIME NOT NULL, INDEX IDX_9474526C7294869C (article_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
23 | $this->addSql('CREATE TABLE article (id INT AUTO_INCREMENT NOT NULL, author_id INT NOT NULL, image_id INT DEFAULT NULL, title VARCHAR(255) NOT NULL, create_at DATETIME NOT NULL, content VARCHAR(255) NOT NULL, INDEX IDX_23A0E66F675F31B (author_id), UNIQUE INDEX UNIQ_23A0E663DA5256D (image_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
24 | $this->addSql('CREATE TABLE article_tag (article_id INT NOT NULL, tag_id INT NOT NULL, INDEX IDX_919694F97294869C (article_id), INDEX IDX_919694F9BAD26311 (tag_id), PRIMARY KEY(article_id, tag_id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
25 | $this->addSql('CREATE TABLE image (id INT AUTO_INCREMENT NOT NULL, alt VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
26 | $this->addSql('ALTER TABLE comment ADD CONSTRAINT FK_9474526C7294869C FOREIGN KEY (article_id) REFERENCES article (id)');
27 | $this->addSql('ALTER TABLE article ADD CONSTRAINT FK_23A0E66F675F31B FOREIGN KEY (author_id) REFERENCES user (id)');
28 | $this->addSql('ALTER TABLE article ADD CONSTRAINT FK_23A0E663DA5256D FOREIGN KEY (image_id) REFERENCES image (id)');
29 | $this->addSql('ALTER TABLE article_tag ADD CONSTRAINT FK_919694F97294869C FOREIGN KEY (article_id) REFERENCES article (id) ON DELETE CASCADE');
30 | $this->addSql('ALTER TABLE article_tag ADD CONSTRAINT FK_919694F9BAD26311 FOREIGN KEY (tag_id) REFERENCES tag (id) ON DELETE CASCADE');
31 | }
32 |
33 | public function down(Schema $schema): void
34 | {
35 | // this down() migration is auto-generated, please modify it to your needs
36 | $this->abortIf('mysql' !== $this->connection->getDatabasePlatform()->getName(), 'Migration can only be executed safely on \'mysql\'.');
37 |
38 | $this->addSql('ALTER TABLE article_tag DROP FOREIGN KEY FK_919694F9BAD26311');
39 | $this->addSql('ALTER TABLE article DROP FOREIGN KEY FK_23A0E66F675F31B');
40 | $this->addSql('ALTER TABLE comment DROP FOREIGN KEY FK_9474526C7294869C');
41 | $this->addSql('ALTER TABLE article_tag DROP FOREIGN KEY FK_919694F97294869C');
42 | $this->addSql('ALTER TABLE article DROP FOREIGN KEY FK_23A0E663DA5256D');
43 | $this->addSql('DROP TABLE tag');
44 | $this->addSql('DROP TABLE user');
45 | $this->addSql('DROP TABLE comment');
46 | $this->addSql('DROP TABLE article');
47 | $this->addSql('DROP TABLE article_tag');
48 | $this->addSql('DROP TABLE image');
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Migrations/Version20180209225711.php:
--------------------------------------------------------------------------------
1 | abortIf('mysql' !== $this->connection->getDatabasePlatform()->getName(), 'Migration can only be executed safely on \'mysql\'.');
19 |
20 | $this->addSql('ALTER TABLE article CHANGE content content LONGTEXT NOT NULL');
21 | }
22 |
23 | public function down(Schema $schema): void
24 | {
25 | // this down() migration is auto-generated, please modify it to your needs
26 | $this->abortIf('mysql' !== $this->connection->getDatabasePlatform()->getName(), 'Migration can only be executed safely on \'mysql\'.');
27 |
28 | $this->addSql('ALTER TABLE article CHANGE content content VARCHAR(255) NOT NULL COLLATE utf8_unicode_ci');
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Migrations/Version20180314174711.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
17 |
18 | $this->addSql('ALTER TABLE article ADD slug VARCHAR(255) NOT NULL');
19 | $this->addSql('CREATE UNIQUE INDEX UNIQ_23A0E66989D9B62 ON article (slug)');
20 | }
21 |
22 | public function down(Schema $schema): void
23 | {
24 | // this down() migration is auto-generated, please modify it to your needs
25 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
26 |
27 | $this->addSql('DROP INDEX UNIQ_23A0E66989D9B62 ON article');
28 | $this->addSql('ALTER TABLE article DROP slug');
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Migrations/Version20180402143231.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
17 |
18 | $this->addSql('ALTER TABLE user ADD reset_password_token VARCHAR(255) DEFAULT NULL');
19 | $this->addSql('CREATE UNIQUE INDEX UNIQ_8D93D649452C9EC5 ON user (reset_password_token)');
20 | }
21 |
22 | public function down(Schema $schema): void
23 | {
24 | // this down() migration is auto-generated, please modify it to your needs
25 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
26 |
27 | $this->addSql('DROP INDEX UNIQ_8D93D649452C9EC5 ON user');
28 | $this->addSql('ALTER TABLE user DROP reset_password_token');
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Migrations/Version20180423195809.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
17 |
18 | $this->addSql('ALTER TABLE user ADD token_created_at DATETIME DEFAULT NULL');
19 | }
20 |
21 | public function down(Schema $schema): void
22 | {
23 | // this down() migration is auto-generated, please modify it to your needs
24 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
25 |
26 | $this->addSql('ALTER TABLE user DROP token_created_at');
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Migrations/Version20180513194510.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
17 |
18 | $this->addSql('ALTER TABLE user CHANGE token_created_at token_expiration_date DATETIME DEFAULT NULL');
19 | }
20 |
21 | public function down(Schema $schema): void
22 | {
23 | // this down() migration is auto-generated, please modify it to your needs
24 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
25 |
26 | $this->addSql('ALTER TABLE user CHANGE token_expiration_date token_created_at DATETIME DEFAULT NULL');
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Migrations/Version20180607202731.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
17 |
18 | $this->addSql('ALTER TABLE user CHANGE password password VARCHAR(255) DEFAULT NULL');
19 | }
20 |
21 | public function down(Schema $schema) : void
22 | {
23 | // this down() migration is auto-generated, please modify it to your needs
24 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
25 |
26 | $this->addSql('ALTER TABLE user CHANGE password password VARCHAR(255) NOT NULL COLLATE utf8_unicode_ci');
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Migrations/Version20180607203541.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
17 |
18 | $this->addSql('ALTER TABLE user ADD provider_id INT DEFAULT NULL');
19 | }
20 |
21 | public function down(Schema $schema) : void
22 | {
23 | // this down() migration is auto-generated, please modify it to your needs
24 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
25 |
26 | $this->addSql('ALTER TABLE user DROP provider_id');
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Migrations/Version20180611080116.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
17 |
18 | $this->addSql('ALTER TABLE comment ADD user_id INT NOT NULL, DROP username, DROP email');
19 | $this->addSql('ALTER TABLE comment ADD CONSTRAINT FK_9474526CA76ED395 FOREIGN KEY (user_id) REFERENCES user (id)');
20 | $this->addSql('CREATE INDEX IDX_9474526CA76ED395 ON comment (user_id)');
21 | }
22 |
23 | public function down(Schema $schema) : void
24 | {
25 | // this down() migration is auto-generated, please modify it to your needs
26 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
27 |
28 | $this->addSql('ALTER TABLE comment DROP FOREIGN KEY FK_9474526CA76ED395');
29 | $this->addSql('DROP INDEX IDX_9474526CA76ED395 ON comment');
30 | $this->addSql('ALTER TABLE comment ADD username VARCHAR(255) NOT NULL COLLATE utf8_unicode_ci, ADD email VARCHAR(255) NOT NULL COLLATE utf8_unicode_ci, DROP user_id');
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Migrations/Version20181224152453.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
17 |
18 | $this->addSql('CREATE TABLE child_comment (id INT AUTO_INCREMENT NOT NULL, comment_id INT NOT NULL, user_id INT NOT NULL, content LONGTEXT NOT NULL, create_at DATETIME NOT NULL, INDEX IDX_F6C01A77F8697D13 (comment_id), INDEX IDX_F6C01A77A76ED395 (user_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB');
19 | $this->addSql('ALTER TABLE child_comment ADD CONSTRAINT FK_F6C01A77F8697D13 FOREIGN KEY (comment_id) REFERENCES comment (id)');
20 | $this->addSql('ALTER TABLE child_comment ADD CONSTRAINT FK_F6C01A77A76ED395 FOREIGN KEY (user_id) REFERENCES user (id)');
21 | $this->addSql('ALTER TABLE article DROP FOREIGN KEY FK_23A0E66F675F31B');
22 | $this->addSql('DROP INDEX IDX_23A0E66F675F31B ON article');
23 | $this->addSql('ALTER TABLE article CHANGE author_id user_id INT NOT NULL');
24 | $this->addSql('ALTER TABLE article ADD CONSTRAINT FK_23A0E66A76ED395 FOREIGN KEY (user_id) REFERENCES user (id)');
25 | $this->addSql('CREATE INDEX IDX_23A0E66A76ED395 ON article (user_id)');
26 | $this->addSql('ALTER TABLE comment CHANGE content content LONGTEXT NOT NULL');
27 | }
28 |
29 | public function down(Schema $schema) : void
30 | {
31 | // this down() migration is auto-generated, please modify it to your needs
32 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
33 |
34 | $this->addSql('DROP TABLE child_comment');
35 | $this->addSql('ALTER TABLE article DROP FOREIGN KEY FK_23A0E66A76ED395');
36 | $this->addSql('DROP INDEX IDX_23A0E66A76ED395 ON article');
37 | $this->addSql('ALTER TABLE article CHANGE user_id author_id INT NOT NULL');
38 | $this->addSql('ALTER TABLE article ADD CONSTRAINT FK_23A0E66F675F31B FOREIGN KEY (author_id) REFERENCES user (id)');
39 | $this->addSql('CREATE INDEX IDX_23A0E66F675F31B ON article (author_id)');
40 | $this->addSql('ALTER TABLE comment CHANGE content content VARCHAR(255) NOT NULL COLLATE utf8_unicode_ci');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Migrations/Version20190913132213.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
17 |
18 | $this->addSql('CREATE TABLE notification (id INT AUTO_INCREMENT NOT NULL, notification_type_id INT NOT NULL, created_by_id INT NOT NULL, created_at DATETIME NOT NULL, target_link VARCHAR(255) NOT NULL, INDEX IDX_BF5476CAD0520624 (notification_type_id), INDEX IDX_BF5476CAB03A8386 (created_by_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB');
19 | $this->addSql('CREATE TABLE notification_type (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB');
20 | $this->addSql('ALTER TABLE notification ADD CONSTRAINT FK_BF5476CAD0520624 FOREIGN KEY (notification_type_id) REFERENCES notification_type (id)');
21 | $this->addSql('ALTER TABLE notification ADD CONSTRAINT FK_BF5476CAB03A8386 FOREIGN KEY (created_by_id) REFERENCES user (id)');
22 | $this->addSql('DROP TABLE child_comment');
23 | }
24 |
25 | public function down(Schema $schema) : void
26 | {
27 | // this down() migration is auto-generated, please modify it to your needs
28 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
29 |
30 | $this->addSql('ALTER TABLE notification DROP FOREIGN KEY FK_BF5476CAD0520624');
31 | $this->addSql('CREATE TABLE child_comment (id INT AUTO_INCREMENT NOT NULL, comment_id INT NOT NULL, user_id INT NOT NULL, content LONGTEXT NOT NULL COLLATE utf8mb4_unicode_ci, create_at DATETIME NOT NULL, INDEX IDX_F6C01A77A76ED395 (user_id), INDEX IDX_F6C01A77F8697D13 (comment_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB COMMENT = \'\' ');
32 | $this->addSql('ALTER TABLE child_comment ADD CONSTRAINT FK_F6C01A77A76ED395 FOREIGN KEY (user_id) REFERENCES user (id)');
33 | $this->addSql('ALTER TABLE child_comment ADD CONSTRAINT FK_F6C01A77F8697D13 FOREIGN KEY (comment_id) REFERENCES comment (id)');
34 | $this->addSql('DROP TABLE notification');
35 | $this->addSql('DROP TABLE notification_type');
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Migrations/Version20190929114309.php:
--------------------------------------------------------------------------------
1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
17 |
18 | $this->addSql('CREATE TABLE user_notification (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, notification_id INT DEFAULT NULL, is_read TINYINT(1) NOT NULL, created_at DATETIME NOT NULL, INDEX IDX_3F980AC8A76ED395 (user_id), INDEX IDX_3F980AC8EF1A9D84 (notification_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB');
19 | $this->addSql('ALTER TABLE user_notification ADD CONSTRAINT FK_3F980AC8A76ED395 FOREIGN KEY (user_id) REFERENCES user (id)');
20 | $this->addSql('ALTER TABLE user_notification ADD CONSTRAINT FK_3F980AC8EF1A9D84 FOREIGN KEY (notification_id) REFERENCES notification (id)');
21 | }
22 |
23 | public function down(Schema $schema) : void
24 | {
25 | // this down() migration is auto-generated, please modify it to your needs
26 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
27 |
28 | $this->addSql('DROP TABLE user_notification');
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Repository/ArticleRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder('p');
24 |
25 | $qb
26 | ->setFirstResult(($page - 1) * $maxResults)
27 | ->setMaxResults($maxResults)
28 | ->orderBy('p.createAt', 'DESC');
29 |
30 | return new Paginator($qb);
31 | }
32 |
33 | public function getArticlesWithComment(): array
34 | {
35 | return $this->createQueryBuilder('a')
36 | ->leftJoin('a.comments', 'c')
37 | ->addSelect('c')
38 | ->orderBy('a.createAt', 'DESC')
39 | ->getQuery()->getResult();
40 | }
41 |
42 | public function countArticles(): string
43 | {
44 | return $this->createQueryBuilder('a')
45 | ->select('COUNT(a)')
46 | ->getQuery()
47 | ->getSingleScalarResult();
48 | }
49 |
50 | public function saveNewArticle(Article $article): void
51 | {
52 | $this->_em->persist($article);
53 | $this->_em->flush();
54 | }
55 |
56 | public function saveExistingArticle(): void
57 | {
58 | $this->_em->flush();
59 | }
60 |
61 | public function remove(Article $article): void
62 | {
63 | $this->_em->remove($article);
64 | $this->_em->flush();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Repository/CommentRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder('c')
19 | ->select('COUNT(c)')
20 | ->getQuery()
21 | ->getSingleScalarResult();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Repository/ImageRepository.php:
--------------------------------------------------------------------------------
1 | _em->persist($notification);
26 | $this->_em->flush();
27 | }
28 |
29 | /**
30 | * Get unread notifications for the current user.
31 | */
32 | public function getUnreadNotification(User $user)
33 | {
34 | $qb = $this->createQueryBuilder('n');
35 | $qb
36 | ->innerJoin('n.userNotifications', 'un')
37 | ->where('un.isRead = 0')
38 | ->andWhere('un.user = :user')
39 | ->setParameter(':user', $user)
40 | ->orderBy('n.createdAt', 'DESC')
41 | ;
42 |
43 | return $qb->getQuery()->getResult();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Repository/NotificationTypeRepository.php:
--------------------------------------------------------------------------------
1 | _em->persist($userNotification);
25 | }
26 |
27 | public function flush()
28 | {
29 | $this->_em->flush();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Repository/UserRepository.php:
--------------------------------------------------------------------------------
1 | createQueryBuilder('u');
19 | $qb->where('u.resetPasswordToken = :token');
20 | $qb->setParameter(':token', $token);
21 |
22 | return $qb->getQuery()->getOneOrNullResult();
23 | }
24 |
25 | public function getByProviderId(string $providerId): ?User
26 | {
27 | $qb = $this->createQueryBuilder('u');
28 | $qb
29 | ->where('u.providerId = :providerId')
30 | ->setParameter(':providerId', $providerId);
31 |
32 | return $qb->getQuery()->getOneOrNullResult();
33 | }
34 |
35 | public function countUsers(): string
36 | {
37 | return $this->createQueryBuilder('u')
38 | ->select('COUNT(u)')
39 | ->getQuery()
40 | ->getSingleScalarResult();
41 | }
42 |
43 | public function save(User $user): void
44 | {
45 | $this->_em->persist($user);
46 | $this->_em->flush();
47 | }
48 |
49 | public function saveNewPassword()
50 | {
51 | $this->_em->flush();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Services/Article/Manager/ArticleManager.php:
--------------------------------------------------------------------------------
1 | tokenStorage = $tokenStorage;
40 | $this->uploader = $uploader;
41 | $this->repository = $repository;
42 | $this->em = $em;
43 | }
44 |
45 | public function create(Article $article): void
46 | {
47 | $article->setAuthor($this->tokenStorage->getToken()->getUser());
48 |
49 | if (null !== $article->getImage()) {
50 | if ($this->uploader->hasNewImage($article->getImage())) {
51 | $this->uploadImage($article);
52 | }
53 | }
54 |
55 | $this->repository->saveNewArticle($article);
56 | }
57 |
58 | public function edit(Article $article)
59 | {
60 | $image = $article->getImage();
61 |
62 | if (null !== $image) {
63 | if ($this->uploader->hasNewImage($image)) {
64 | if ($this->uploader->hasActiveImage($image)) {
65 | $this->uploader->removeImage($image->getAlt());
66 | }
67 | $this->uploadImage($article);
68 | } else {
69 | if ($this->uploader->hasActiveImage($image) && $this->uploader->isDeleteImageChecked($image)) {
70 | $this->uploader->removeImage($image->getAlt());
71 | $this->em->remove($image);
72 | $article->setImage(null);
73 | }
74 | }
75 | }
76 |
77 | $this->repository->saveExistingArticle();
78 | }
79 |
80 | public function remove(Article $article): void
81 | {
82 | $this->repository->remove($article);
83 | }
84 |
85 | private function uploadImage(Article $article): void
86 | {
87 | $alt = $this->uploader->generateAlt($article->getImage()->getFile());
88 | $article->getImage()->setAlt($alt);
89 | $this->uploader->uploadImage($article->getImage()->getFile(), $alt);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/Services/Faker/Provider/EncodePasswordProvider.php:
--------------------------------------------------------------------------------
1 | encoder = $encoder;
18 | }
19 |
20 | public function encodePassword(User $user, string $plainPassword)
21 | {
22 | return $this->encoder->encodePassword($user, $plainPassword);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Services/FlashMessage.php:
--------------------------------------------------------------------------------
1 | getSession()->getFlashBag()->add($type, $message);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Services/Mailer.php:
--------------------------------------------------------------------------------
1 | mailer = $mailer;
15 | }
16 |
17 | public function buildAndSendMail(string $subject, $recever, $body)
18 | {
19 | $message = (new \Swift_Message($subject))
20 | ->setFrom('no-remply@symfony-blog.com')
21 | ->setTo($recever)
22 | ->setBody($body, 'text/html');
23 |
24 | $this->send($message);
25 | }
26 |
27 | private function send($message)
28 | {
29 | $this->mailer->send($message);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Services/MercureCookieGenerator.php:
--------------------------------------------------------------------------------
1 | secret = $secret;
20 | }
21 |
22 | public function generate(): string
23 | {
24 | $token = (new Builder())
25 | ->set('mercure', ['subscribe' => ['http://symfony-blog.fr/group/users']])
26 | ->sign(new Sha256(), $this->secret)
27 | ->getToken();
28 |
29 | return sprintf('%s=%s; path=hub/; httponly;', self::MERCURE_AUTHORIZATION_HEADER, $token);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Services/Notification/Factory/NotificationFactory.php:
--------------------------------------------------------------------------------
1 | notificationTypeRepository = $notificationTypeRepository;
33 | $this->storage = $storage;
34 | $this->urlGenerator = $urlGenerator;
35 | }
36 |
37 | public function create(Article $article, string $notificationTypeName): Notification
38 | {
39 | $notificationType = $this->notificationTypeRepository->findOneBy(['name' => $notificationTypeName]);
40 | $url = $this->urlGenerator->generate('article_show', ['slug' => $article->getSlug()]);
41 |
42 | $notification = (new Notification())
43 | ->setNotificationType($notificationType)
44 | ->setCreatedBy($this->storage->getToken()->getUser())
45 | ->setTargetLink($url);
46 |
47 | return $notification;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Services/Notifier.php:
--------------------------------------------------------------------------------
1 | notificationFactory = $notificationFactory;
60 | $this->notificationRepository = $notificationRepository;
61 | $this->serializer = $serializer;
62 | $this->userRepository = $userRepository;
63 | $this->userNotificationFactory = $userNotificationFactory;
64 | $this->userNotificationRepository = $userNotificationRepository;
65 | }
66 |
67 | /**
68 | * When an article is created, the app will notify all users that are connected.
69 | */
70 | public function articleCreated(Article $article, Publisher $publisher)
71 | {
72 | $notification = $this->notificationFactory->create($article, NotificationType::ARTICLE_CREATED);
73 | $this->notificationRepository->save($notification);
74 |
75 | $jsonContent = $this->serializer->serialize($notification, 'json');
76 |
77 | $users = $this->userRepository->findAll();
78 |
79 | foreach ($users as $user) {
80 | $userNotification = $this->userNotificationFactory->create($notification, $user);
81 | $this->userNotificationRepository->persist($userNotification);
82 | }
83 |
84 | $this->userNotificationRepository->flush();
85 |
86 | $update = new Update(
87 | 'http://symfony-blog.fr/new/article',
88 | $jsonContent,
89 | ['http://symfony-blog.fr/group/users']
90 | );
91 |
92 | $publisher($update);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/Services/Paginator.php:
--------------------------------------------------------------------------------
1 | itemPerPage = $itemPerPage;
22 | $this->requestStack = $requestStack;
23 | }
24 |
25 | public function getItemList($repository, $page)
26 | {
27 | return $repository->paginator($page, $this->itemPerPage);
28 | }
29 |
30 | public function countPage($items): int
31 | {
32 | return ceil(\count($items) / $this->itemPerPage);
33 | }
34 |
35 | public function getPage(): int
36 | {
37 | $request = $this->requestStack->getCurrentRequest();
38 |
39 | $page = $request->query->get('page');
40 |
41 | if ($page < 1) {
42 | $page = 1;
43 | }
44 |
45 | return $page;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Services/TokenPassword.php:
--------------------------------------------------------------------------------
1 | em = $em;
35 | $this->generator = $generator;
36 | }
37 |
38 | /**
39 | * Generate and add a temporary token to the target user to allow a reset password.
40 | */
41 | public function addToken(User $user): void
42 | {
43 | $this->token = $this->generateToken();
44 | $user->setResetPasswordToken($this->token);
45 | $user->setTokenExpirationDate();
46 | $this->em->flush();
47 | }
48 |
49 | private function generateToken(): string
50 | {
51 | return $this->generator->generateToken();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Services/Uploader.php:
--------------------------------------------------------------------------------
1 | targetDir = $targetDir;
16 | }
17 |
18 | public function hasNewImage(Image $image): bool
19 | {
20 | return null !== $image->getFile() && $image->getFile() instanceof UploadedFile;
21 | }
22 |
23 | public function hasActiveImage(Image $image): bool
24 | {
25 | return null !== $image->getId();
26 | }
27 |
28 | public function isDeleteImageChecked(Image $image): bool
29 | {
30 | return $image->isDeletedImage();
31 | }
32 |
33 | public function generateAlt(UploadedFile $file): string
34 | {
35 | return md5(uniqid('', true)).'.'.$file->guessExtension();
36 | }
37 |
38 | public function uploadImage(UploadedFile $file, string $imageName): void
39 | {
40 | $file->move($this->getTargetDir(), $imageName);
41 | }
42 |
43 | public function removeImage(string $imageName): void
44 | {
45 | $fs = new Filesystem();
46 |
47 | if ($fs->exists($this->getTargetDir().$imageName)) {
48 | unlink($this->getTargetDir().$imageName);
49 | }
50 | }
51 |
52 | private function getTargetDir(): string
53 | {
54 | return $this->targetDir;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Services/User/Manager/UserManager.php:
--------------------------------------------------------------------------------
1 | repository = $repository;
70 | $this->checker = $checker;
71 | $this->eventDispatcher = $eventDispatcher;
72 | $this->mailer = $mailer;
73 | $this->trans = $trans;
74 | $this->templating = $templating;
75 | $this->requestStack = $requestStack;
76 | $this->encoder = $encoder;
77 | }
78 |
79 | public function create(User $user): void
80 | {
81 | $user->setPassword($this->encodePassword($user));
82 | $this->repository->save($user);
83 | }
84 |
85 | public function resetPassword(User $user): void
86 | {
87 | $genericEvent = new GenericEvent($user);
88 | $this->eventDispatcher->dispatch(Events::TOKEN_RESET, $genericEvent);
89 |
90 | $user->setPassword($this->encodePassword($user));
91 | $this->repository->saveNewPassword();
92 | }
93 |
94 | public function sendPasswordRequestEmail(User $user)
95 | {
96 | $this->mailer->buildAndSendMail(
97 | $this->trans->trans('reset_password.title', [], 'emails'),
98 | $user->getEmail(),
99 | $this
100 | ->templating
101 | ->render('email/password_request/_password_reset_email_'.
102 | $this->requestStack->getMasterRequest()->getLocale().'.html.twig', [
103 | 'username' => $user->getUsername(),
104 | 'token' => $user->getResetPasswordToken(),
105 | ])
106 | );
107 | }
108 |
109 | public function isLogin(): bool
110 | {
111 | return $this->checker->isGranted('IS_AUTHENTICATED_FULLY');
112 | }
113 |
114 | public function isTokenExpired(User $user): bool
115 | {
116 | return $user->getTokenExpirationDate() < new \DateTime();
117 | }
118 |
119 | private function encodePassword(User $user): string
120 | {
121 | return $this->encoder->encodePassword($user, $user->getPlainPassword());
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/Services/UserActionLogger.php:
--------------------------------------------------------------------------------
1 | logger = $userActionLogger;
17 | }
18 |
19 | public function userAction(string $entityName, string $status): void
20 | {
21 | $this->logger->info('User '.$status.' : '.$entityName);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Services/UserNotification/Factory/UserNotificationFactory.php:
--------------------------------------------------------------------------------
1 | setUser($user);
15 | $notification->addUserNotification($userNotification);
16 |
17 | return $userNotification;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Twig/NotificationExtension.php:
--------------------------------------------------------------------------------
1 | notificationRepository = $notificationRepository;
26 | $this->tokenStorage = $tokenStorage;
27 | }
28 |
29 | /**
30 | * Returns unread notifications to the twig global variable 'notifications'.
31 | *
32 | * @return array
33 | */
34 | public function getNotifications()
35 | {
36 | $user = $this->tokenStorage->getToken()->getUser();
37 | $notifications = $this->notificationRepository->getUnreadNotification($user);
38 |
39 | return $notifications;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/templates/backoffice/article/add.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'backoffice/layout-backoffice.html.twig' %}
2 |
3 | {% block body %}
4 | {% set titlePage = 'backoffice.articles.add.title'|trans %}
5 |
6 |
7 |
8 |
{{ titlePage }}
9 |
10 |
11 | {{ form_start(form) }}
12 | {{ form_errors(form) }}
13 |
14 |
{{ 'backoffice.articles.add_edit_form.actuality'|trans }}
15 |
16 | {{ form_label(form.title, 'backoffice.articles.add_edit_form.article_title'|trans, {'label_attr': {'class': 'label'}}) }}
17 |
18 | {{ form_widget(form.title, {'attr': {'class': 'input'}}) }}
19 |
20 |
21 | {{ form_errors(form.title) }}
22 |
23 |
24 |
25 |
26 | {{ form_label(form.content, 'backoffice.articles.add_edit_form.article_content'|trans, {'label_attr': {'class': 'label'}}) }}
27 |
28 | {{ form_widget(form.content, {'attr': {'class': 'textarea'}}) }}
29 |
30 |
31 | {{ form_errors(form.content) }}
32 |
33 |
34 |
35 |
36 | {{ form_label(form.tags, 'backoffice.articles.add_edit_form.article_tags'|trans, {'label_attr': {'class': 'label'}}) }}
37 |
38 | {{ form_widget(form.tags, {'attr': {'class': 'hidden-tag-input'}}) }}
39 |
40 |
41 |
42 |
43 |
44 | {{ form_errors(form.tags) }}
45 |
46 |
{{ 'backoffice.articles.add_edit_form.article_tags_helper'|trans }}
47 |
48 |
49 |
{{ 'backoffice.articles.add_edit_form.article_media'|trans }}
50 |
51 |
52 | {{ form_widget(form.image) }}
53 |
54 | {{ form_errors(form.image) }}
55 |
56 |
57 |
58 | {{ form_rest(form) }}
59 |
60 |
61 |
62 | {{ 'backoffice.articles.add_edit_form.submit_button'|trans }}
63 |
64 |
65 | {{ form_end(form) }}
66 |
67 | {% endblock %}
68 |
69 | {% block javascripts %}
70 | {{ parent() }}
71 |
72 | {% endblock %}
73 |
--------------------------------------------------------------------------------
/templates/backoffice/article/list.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'backoffice/layout-backoffice.html.twig' %}
2 |
3 | {% block body %}
4 | {% set titlePage = 'backoffice.articles.list.title'|trans %}
5 |
6 |
7 |
8 |
9 |
{{ titlePage }}
10 |
11 |
12 |
13 |
14 | {% include 'flashMessage/flashMessage.html.twig' %}
15 |
16 |
17 |
18 |
25 |
26 |
27 |
28 | {% if articles is not empty %}
29 |
30 |
31 |
32 | {{ 'backoffice.articles.list.article_title'|trans }}
33 | {{ 'backoffice.articles.list.article_author'|trans }}
34 | {{ 'backoffice.articles.list.article_posted_at'|trans }}
35 | {{ 'backoffice.articles.list.article_comment'|trans }}
36 | {{ 'backoffice.articles.list.articlr_action'|trans }}
37 |
38 |
39 |
40 | {% for article in articles %}
41 |
42 | {{ article.title }}
43 | {{ article.author.username }}
44 | {{ article.createAt|date("d/m/Y") }}
45 | {{ article.comments.count }}
46 |
47 |
49 |
50 |
51 |
53 |
54 |
55 |
58 |
59 |
60 |
61 |
62 | {% endfor %}
63 |
64 |
65 |
66 | {% else %}
67 |
{{ 'backoffice.articles.list.no_article'|trans }}
68 | {% endif %}
69 |
70 |
71 |
72 | {% endblock %}
73 |
74 | {% block javascripts %}
75 | {{ parent() }}
76 |
77 | {% endblock %}
78 |
--------------------------------------------------------------------------------
/templates/backoffice/dashboard/dashboard.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'backoffice/layout-backoffice.html.twig' %}
2 |
3 | {% block body %}
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ countArticles }} {{ countArticles <= 1 ? 'backoffice.dashboard.article'|trans : 'backoffice.dashboard.articles'|trans }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | {{ countComments }} {{ countComments <= 1 ? 'backoffice.dashboard.comment'|trans : 'backoffice.dashboard.comments'|trans }}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | {{ countUsers }} {{ countUsers <= 1 ? 'backoffice.dashboard.user'|trans : 'backoffice.dashboard.users'|trans }}
29 |
30 |
31 |
32 |
33 |
34 | {% endblock %}
35 |
--------------------------------------------------------------------------------
/templates/backoffice/layout-backoffice.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'base.html.twig' %}
2 |
3 | {% block wrapper %}
4 | {{ parent() }}
5 |
6 | {% include 'backoffice/sidebar/sidebar.html.twig' %}
7 |
8 |
9 | {% block body %}{% endblock %}
10 |
11 |
12 | {% endblock %}
13 |
--------------------------------------------------------------------------------
/templates/backoffice/sidebar/sidebar.html.twig:
--------------------------------------------------------------------------------
1 |
19 |
20 |
--------------------------------------------------------------------------------
/templates/base.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {% block title %}Symfony-blog{% endblock %}
6 |
7 | {% block stylesheets %}
8 |
9 | {% endblock %}
10 |
11 |
12 | {% block wrapper %}
13 | {% include 'navbar/navbar.html.twig' %}
14 | {% include 'notification-translations/message.html.twig' %}
15 | {% endblock %}
16 |
17 | {% block javascripts %}
18 | {% if is_granted('ROLE_USER') %}
19 |
20 | {% endif %}
21 |
22 | {% endblock %}
23 |
24 |
--------------------------------------------------------------------------------
/templates/blog/article/_comment_form.html.twig:
--------------------------------------------------------------------------------
1 | {{ form_start(form, {'action': path('comment_new', {'slug': article.slug}), 'attr': {'id': 'comment-form'} }) }}
2 | {{ form_errors(form) }}
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | {{ form_label(form.content, 'article.comment'|trans, {'label_attr': {'class': 'label'}}) }}
13 |
14 | {{ form_widget(form.content, {'attr': {'class': 'textarea', 'placeholder': 'article.form.comment_placeholder'|trans}}) }}
15 |
16 |
17 |
18 |
19 | {{ form_rest(form) }}
20 | {{ 'article.form.post_comment'|trans }}
21 |
22 |
23 |
24 |
25 | {{ form_end(form) }}
26 |
--------------------------------------------------------------------------------
/templates/blog/article/show.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'blog/layout-blog.html.twig' %}
2 |
3 | {% block body %}
4 | {% if article.image is not null %}
5 |
6 | {% endif %}
7 |
8 | {{ article.title }}
9 |
10 |
11 | {{ article.createAt|date("d/m/Y") }}
12 | {{ 'generic.by'|trans }} {{ article.author.username|capitalize }}
13 | {{ article.comments.count }}
14 | {{ article.comments.count <= 1 ? 'article.comment'|trans : 'article.comments'|trans }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
{{ article.content }}
22 |
23 |
24 |
25 |
26 | {{ article.comments|length }} {{ article.comments|length <= 1 ? 'article.comment'|trans : 'article.comments'|trans }}
27 |
28 |
29 | {% for comment in article.comments|sort|reverse %}
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | {{ comment.user.username }}
40 |
41 | {{ comment.content }}
42 |
43 |
44 |
45 |
55 |
56 |
57 |
58 |
59 | {% endfor %}
60 |
61 |
62 |
63 | {% if is_granted('ROLE_USER') %}
64 | {{ render(controller('App\\Controller\\BlogController::commentForm', {'article': article.id})) }}
65 | {% else %}
66 |
72 | {% endif %}
73 |
74 |
75 | {% endblock %}
76 |
77 |
--------------------------------------------------------------------------------
/templates/blog/home/index.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'blog/layout-blog.html.twig' %}
2 |
3 | {% block body %}
4 | {% if articles is not empty %}
5 | {% for article in articles %}
6 |
7 |
12 |
13 |
14 | {{ article.content|truncate(50) }}
15 |
16 | {{ article.createAt|date("d/m/Y") }}
17 | {{ 'generic.by'|trans }} {{ article.author.username }}
18 |
19 |
20 |
25 |
26 | {% endfor %}
27 |
28 | {% include 'paginator/paginator.html.twig' %}
29 | {% else %}
30 |
31 |
{{ 'home.no_article'|trans }}
32 |
33 | {% endif %}
34 | {% endblock %}
35 |
--------------------------------------------------------------------------------
/templates/blog/layout-blog.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'base.html.twig' %}
2 |
3 | {% block wrapper %}
4 | {{ parent() }}
5 |
6 |
7 | {% block flashmessage %}
8 | {% include 'flashMessage/flashMessage.html.twig' %}
9 | {% endblock %}
10 |
11 | {% block body %}{% endblock %}
12 |
13 | {% block security %}{% endblock %}
14 |
15 |
16 |
17 | {% block footer %}
18 |
33 | {% endblock %}
34 |
35 | {% endblock %}
36 |
--------------------------------------------------------------------------------
/templates/blog/security/login/_login_form.html.twig:
--------------------------------------------------------------------------------
1 | {{ form_start(form, {'attr': {'id': 'login-form'}}) }}
2 | {{ form_errors(form) }}
3 |
4 |
5 | {{ form_label(form.username, 'login.form.username'|trans, {'label_attr': {'class': 'label has-text-left'}}) }}
6 |
7 | {{ form_widget(form.username, {'value': lastUserName, 'attr': {'class': 'input'}}) }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | {{ form_label(form.password, 'login.form.password'|trans, {'label_attr': {'class': 'label has-text-left'}}) }}
16 |
17 | {{ form_widget(form.password, {'attr': {'class': 'input'}}) }}
18 |
19 |
20 |
21 |
22 |
23 |
24 | {% if (error) %}
25 |
26 | {{ error.messageKey|trans(error.messageData, 'messages') }}
27 |
28 | {% endif %}
29 |
30 |
31 | {{ 'login.login'|trans }}
32 |
33 | {{ form_end(form) }}
34 |
--------------------------------------------------------------------------------
/templates/blog/security/login/login.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'blog/layout-blog.html.twig' %}
2 |
3 | {% block security %}
4 |
5 |
6 |
{{ 'login.login'|trans }}
7 |
22 |
23 |
24 | {% endblock %}
25 |
--------------------------------------------------------------------------------
/templates/blog/security/password/_password_reset_new_form.html.twig:
--------------------------------------------------------------------------------
1 | {{ form_start(form, {'attr': {'id': 'password-reset-new-form'}}) }}
2 |
3 | {{ form_label(form.plainPassword.first, 'reset_password.form.new_password'|trans, {'label_attr': {'class': 'label has-text-left'}}) }}
4 |
5 | {{ form_widget(form.plainPassword.first, {'attr': {'class': 'input', 'placeholder': 'reset_password.form.new_password_placeholder'|trans}}) }}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{ form_label(form.plainPassword.second, 'reset_password.form.repeat_password'|trans, {'label_attr': {'class': 'label has-text-left'}}) }}
14 |
15 | {{ form_widget(form.plainPassword.second, {'attr': {'class': 'input', 'placeholder': 'reset_password.form.repeat_password_placeholder'|trans}}) }}
16 |
17 |
18 |
19 |
20 |
21 | {{ form_errors(form.plainPassword.first) }}
22 |
23 |
24 |
25 |
26 | {{ 'reset_password.form.submit'|trans }}
27 |
28 | {{ form_end(form) }}
29 |
--------------------------------------------------------------------------------
/templates/blog/security/password/_password_reset_request_form.html.twig:
--------------------------------------------------------------------------------
1 | {{ form_start(form, {'attr': {'id': 'password-reset-request-form'}}) }}
2 |
3 |
4 | {{ 'forgot_password.instruction'|trans }} :
5 |
6 | {{ form_widget(form.email, {'attr': {'class': 'input'}}) }}
7 |
8 | {{ form_errors(form.email) }}
9 | {{ form_errors(form) }}
10 |
11 |
12 |
13 |
14 | {{ 'forgot_password.send'|trans }}
15 |
16 | {{ form_end(form) }}
17 |
--------------------------------------------------------------------------------
/templates/blog/security/password/password_reset_new.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'blog/layout-blog.html.twig' %}
2 |
3 | {% block security %}
4 |
5 |
6 |
{{ 'reset_password.reset_password'|trans }}
7 |
8 | {% include 'blog/security/password/_password_reset_new_form.html.twig' %}
9 |
10 |
11 |
12 | {% endblock %}
13 |
--------------------------------------------------------------------------------
/templates/blog/security/password/password_reset_request.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'blog/layout-blog.html.twig' %}
2 |
3 | {% block security %}
4 |
5 |
6 |
{{ 'forgot_password.forgot_password'|trans }}
7 |
8 | {% include 'blog/security/password/_password_reset_request_form.html.twig' %}
9 |
10 |
11 |
12 | {% endblock %}
13 |
--------------------------------------------------------------------------------
/templates/blog/security/registration/_registration_form.html.twig:
--------------------------------------------------------------------------------
1 | {{ form_start(form, {'attr': {'id': 'registration-form'}}) }}
2 | {{ form_errors(form) }}
3 |
4 | {{ form_label(form.username, 'signin.form.username'|trans, {'label_attr': {'class': 'label has-text-left'}}) }}
5 |
6 | {{ form_widget(form.username, {'attr': {'class': 'input', 'placeholder': 'johnDoe'}}) }}
7 |
8 |
9 |
10 |
11 |
12 | {{ form_errors(form.username) }}
13 |
14 |
15 |
16 |
17 | {{ form_label(form.email, 'signin.form.email'|trans, {'label_attr': {'class': 'label has-text-left'}}) }}
18 |
19 | {{ form_widget(form.email, {'attr': {'class': 'input', 'placeholder': 'you@mail.com'}}) }}
20 |
21 |
22 |
23 |
24 |
25 | {{ form_errors(form.email) }}
26 |
27 |
28 |
29 |
30 | {{ form_label(form.plainPassword.first, 'signin.form.password'|trans, {'label_attr': {'class': 'label has-text-left'}}) }}
31 |
32 | {{ form_widget(form.plainPassword.first, {'attr': {'class': 'input', 'placeholder': 'signin.form.password_placeholder'|trans}}) }}
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | {{ form_label(form.plainPassword.second, 'signin.form.repeat_password'|trans, {'label_attr': {'class': 'label has-text-left'}}) }}
41 |
42 | {{ form_widget(form.plainPassword.second, {'attr': {'class': 'input', 'placeholder': 'signin.form.repeat_password_placeholder'|trans}}) }}
43 |
44 |
45 |
46 |
47 |
48 | {{ form_errors(form.plainPassword.first) }}
49 |
50 |
51 |
52 |
53 | {{ 'signin.signin'|trans }}
54 |
55 | {{ form_end(form) }}
56 |
--------------------------------------------------------------------------------
/templates/blog/security/registration/registration.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'blog/layout-blog.html.twig' %}
2 |
3 | {% block security %}
4 |
5 |
6 |
{{ 'signin.signin'|trans }}
7 |
16 |
17 |
18 | {% endblock %}
19 |
--------------------------------------------------------------------------------
/templates/blog/user/profile/_sidebar.html.twig:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/templates/blog/user/profile/show.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'blog/layout-blog.html.twig' %}
2 |
3 | {% block body %}
4 |
5 |
6 | {% include('blog/user/profile/_sidebar.html.twig') %}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
{{ user.username }}
14 |
{{ user.email }}
15 |
16 |
17 |
18 |
19 |
{{ 'profile.last_comments'|trans }}
20 |
coming soon
21 |
22 |
23 |
24 |
{{ 'profile.badges'|trans }}
25 |
coming soon
26 |
27 |
28 |
29 |
30 | {% endblock %}
31 |
--------------------------------------------------------------------------------
/templates/email/password_request/_password_reset_email_en.html.twig:
--------------------------------------------------------------------------------
1 | Hello {{ username }},
2 | You received this email because a reset password request has been send on your account.
3 | To reset it click on this link below :
4 |
5 |
6 |
7 | {{ url('password_reset_new', {'resetPasswordToken': token}) }}
8 |
9 |
10 |
11 | If you are not the owner of this request, please ignore it
12 |
--------------------------------------------------------------------------------
/templates/email/password_request/_password_reset_email_fr.html.twig:
--------------------------------------------------------------------------------
1 | Bonjour {{ username }},
2 | Vous recevez cet email car une demande de réinitialisation de mot de passe a été demandé pour votre compte.
3 | Vous pouvez le réinitialiser en cliquant sur ce lien :
4 |
5 |
6 |
7 | {{ url('password_reset_new', {'resetPasswordToken': token}) }}
8 |
9 |
10 |
11 | Si vous n'êtes pas à l'origine de cette demande, merci d'ignorer cet email
12 |
--------------------------------------------------------------------------------
/templates/flashMessage/flashMessage.html.twig:
--------------------------------------------------------------------------------
1 | {% for flashMessage in app.session.flashbag.get('info') %}
2 |
3 |
4 |
5 |
6 | {{ flashMessage }}
7 |
8 |
9 | {% endfor %}
10 |
11 | {% for flashMessage in app.session.flashbag.get('error') %}
12 |
13 |
14 |
15 |
16 | {{ flashMessage }}
17 |
18 |
19 | {% endfor %}
20 |
--------------------------------------------------------------------------------
/templates/navbar/bell-notification.html.twig:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
29 |
--------------------------------------------------------------------------------
/templates/navbar/navbar.html.twig:
--------------------------------------------------------------------------------
1 |
3 |
62 |
63 |
--------------------------------------------------------------------------------
/templates/notification-translations/message.html.twig:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/templates/paginator/paginator.html.twig:
--------------------------------------------------------------------------------
1 | {% set minLimit = page - 3 %}
2 | {% set maxLimit = page + 3 %}
3 | {% set hellipMin = 0 %}
4 | {% set hellipMax = 0 %}
5 | {% set arrayPages = range(1, nbPages) %}
6 |
7 |
36 |
--------------------------------------------------------------------------------
/tests/BaseTestCase.php:
--------------------------------------------------------------------------------
1 | selectLink('Log in')->link();
14 | $crawler = $client->click($link);
15 |
16 | $form = $crawler->selectButton('Log in')->form();
17 |
18 | $form['login[username]'] = $username;
19 | $form['login[password]'] = $password;
20 |
21 | return $client->submit($form);
22 | }
23 |
24 | public function logout(Client $client)
25 | {
26 | $client->request('GET', '/logout');
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tests/Functional/ArticleActionTest.php:
--------------------------------------------------------------------------------
1 | request('GET', '/');
17 | $crawler = $this->loginAs($client, $crawler, 'admin', 'azerty');
18 |
19 | $crawler = $client->click($crawler->selectLink('Dashboard')->link());
20 | $crawler = $client->click($crawler->selectLink('ARTICLES')->link());
21 | $crawler = $client->click($crawler->selectLink('New article')->link());
22 |
23 | $form = $crawler->selectButton('Publish')->form();
24 |
25 | $form['App_article[title]'] = 'An awesome new article';
26 | $form['App_article[content]'] = 'This is an article wrote by panther';
27 | $form['app_tag_input'] = 'symfony,panther,';
28 | $crawler = $client->submit($form);
29 |
30 | // $this->assertEquals('An awesome new article', $crawler->filter('.table > tbody > tr > td')->first()->text());
31 |
32 | $this->logout($client);
33 | }
34 |
35 | public function testEditAnArticle()
36 | {
37 | $client = static::createPantherClient();
38 |
39 | $crawler = $client->request('GET', '/');
40 |
41 | $crawler = $this->loginAs($client, $crawler, 'admin', 'azerty');
42 | $crawler = $client->click($crawler->selectLink('Dashboard')->link());
43 | $crawler = $client->click($crawler->selectLink('ARTICLES')->link());
44 | $crawler = $client->click($crawler->filter('a.is-warning')->eq(3)->link());
45 |
46 | $form = $crawler->selectButton('Publish')->form();
47 |
48 | $form['App_article[title]'] = 'Edited by panther';
49 |
50 | $crawler = $client->submit($form);
51 |
52 | $form = $crawler->selectButton('Publish')->form();
53 | $formValues = $form->getValues();
54 |
55 | $this->assertEquals('Edited by panther', $formValues['App_article[title]']);
56 |
57 | $this->logout($client);
58 | }
59 |
60 | // Todo: https://github.com/symfony/panther/issues/203
61 | // public function testDeleteAnArticle()
62 | // {
63 | // $client = static::createPantherClient();
64 | //
65 | // $crawler = $client->request('GET', '/');
66 | //
67 | // $crawler = $this->loginAs($client, $crawler, 'admin', 'azerty');
68 | //
69 | // $crawler = $client->click($crawler->selectLink('Dashboard')->link());
70 | // $crawler = $client->click($crawler->selectLink('ARTICLES')->link());
71 | //
72 | // $client->getWebDriver()->switchTo()->alert()->accept();
73 | //
74 | // $crawler = $client->click($crawler->filter('a.is-danger')->eq(5)->link());
75 | //
76 | // $client->waitFor('.notification');
77 | //
78 | // $this->assertContains('The article has been successfully deleted', $crawler->filter('.notification')->text());
79 | // }
80 | }
81 |
--------------------------------------------------------------------------------
/tests/Functional/CommentTest.php:
--------------------------------------------------------------------------------
1 | request('GET', '/');
14 |
15 | $link = $crawler->selectLink('Read more')->link();
16 | $crawler = $client->click($link);
17 |
18 | $this->assertContains('You must be login to leave a comment', $crawler->filter('.notification')->text());
19 | }
20 |
21 | public function testCommentAnArticle()
22 | {
23 | $client = static::createPantherClient();
24 |
25 | $crawler = $client->request('GET', '/');
26 |
27 | $crawler = $this->loginAs($client, $crawler, 'johnDoe', 'password');
28 |
29 | $link = $crawler->selectLink('Read more')->link();
30 | $crawler = $client->click($link);
31 |
32 | $form = $crawler->selectButton('Leave a comment')->form();
33 |
34 | $form['App_comment[content]'] = 'Hi ! I am a new comment !';
35 | $crawler = $client->submit($form);
36 |
37 | $this->assertContains('Hi ! I am a new comment !', $crawler->filter('.content')->text());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tests/Functional/LoginTest.php:
--------------------------------------------------------------------------------
1 | request('GET', '/login');
14 | $form = $crawler->selectButton('Log in')->form();
15 |
16 | $form['login[username]'] = 'bob';
17 | $form['login[password]'] = 'password';
18 |
19 | $crawler = $client->submit($form);
20 |
21 | $this->assertContains('User or password could not be found', $crawler->filter('.login-message')->text());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/tests/Functional/RouteTest.php:
--------------------------------------------------------------------------------
1 | request('GET', '/');
14 |
15 | $this->assertContains('Training Symfony-blog', $crawler->filter('a')->text());
16 | }
17 |
18 | public function testLogInPage()
19 | {
20 | $client = static::createPantherClient();
21 |
22 | $crawler = $client->request('GET', '/login');
23 |
24 | $this->assertContains('Log in', $crawler->filter('h1')->text());
25 | }
26 |
27 | public function testSignInPage()
28 | {
29 | $client = static::createPantherClient();
30 |
31 | $crawler = $client->request('GET', '/registration');
32 |
33 | $this->assertContains('Sign in', $crawler->filter('h1')->text());
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tests/Unit/Form/DataTransformer/TagsTransformerTest.php:
--------------------------------------------------------------------------------
1 | createTag('Symfony'),
17 | $this->createTag('Test'),
18 | $this->createTag('Unit'),
19 | ];
20 |
21 | $tagsTransformed = $this->getMockedTransformer()->transform($tagsArray);
22 |
23 | $this->assertEquals('Symfony, Test, Unit', $tagsTransformed);
24 | }
25 |
26 | public function testTrimName()
27 | {
28 | $tag = $this->getMockedTransformer()->reverseTransform(' Symfony ');
29 | $this->assertEquals('Symfony', $tag[0]->getName());
30 | }
31 |
32 | public function testDuplicateTagsName()
33 | {
34 | $tags = $this->getMockedTransformer()->reverseTransform('Hello,Hello,Symfony,Symfony,Test,Symfony');
35 | $this->assertCount(3, $tags);
36 | }
37 |
38 | public function testAlreadyDefinedTags()
39 | {
40 | $tagArray = [
41 | $this->createTag('Symfony'),
42 | $this->createTag('Unit'),
43 | ];
44 |
45 | $tags = $this->getMockedTransformer($tagArray)->reverseTransform('Symfony,Unit,Feature,Docker');
46 | $this->assertCount(4, $tags);
47 | $this->assertSame($tagArray[0], $tags[0]);
48 | $this->assertSame($tagArray[1], $tags[1]);
49 | }
50 |
51 | public function testRemoveEmptyTags()
52 | {
53 | $tags = $this->getMockedTransformer()->reverseTransform('Unit, , , ,,Symfony');
54 | $this->assertCount(2, $tags);
55 | $this->assertEquals('Symfony', $tags[1]);
56 | }
57 |
58 | private function getMockedTransformer(array $findByReturnValues = []): TagsTransformer
59 | {
60 | $tagRepository = $this
61 | ->getMockBuilder(EntityRepository::class)
62 | ->disableOriginalConstructor()
63 | ->getMock();
64 | $tagRepository->expects($this->any())
65 | ->method('findBy')
66 | ->willReturn($findByReturnValues);
67 |
68 | $entityManager = $this
69 | ->getMockBuilder(ObjectManager::class)
70 | ->disableOriginalConstructor()
71 | ->getMock();
72 | $entityManager->expects($this->any())
73 | ->method('getRepository')
74 | ->willReturn($tagRepository);
75 |
76 | return new TagsTransformer($entityManager);
77 | }
78 |
79 | private function createTag($name): Tag
80 | {
81 | $tag = new Tag();
82 | $tag->setName($name);
83 |
84 | return $tag;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/tests/Unit/Services/PaginatorTest.php:
--------------------------------------------------------------------------------
1 | createArticle(),
16 | $this->createArticle(),
17 | $this->createArticle(),
18 | $this->createArticle(),
19 | $this->createArticle(),
20 | $this->createArticle(),
21 | ];
22 |
23 | $this->assertEquals($this->getMockedPaginator()->countPage($articleArray), '2');
24 | }
25 |
26 | private function getMockedPaginator()
27 | {
28 | $requestStack = $this
29 | ->getMockBuilder(RequestStack::class)
30 | ->disableOriginalConstructor()
31 | ->getMock();
32 |
33 | return new Paginator($requestStack, '5');
34 | }
35 |
36 | private function createArticle()
37 | {
38 | $article = new Article();
39 |
40 | return $article;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/Unit/Services/TokenPasswordTest.php:
--------------------------------------------------------------------------------
1 | getEntityManagerMock();
16 | $em
17 | ->expects($this->once())
18 | ->method('flush')
19 | ;
20 | $tokenGenerator = $this->getTokenGeneratorMock();
21 | $tokenGenerator
22 | ->expects($this->any())
23 | ->method('generateToken')
24 | ->willReturn('token-genereted')
25 | ;
26 |
27 | $user = new User();
28 |
29 | $resetPassword = new TokenPassword($em, $tokenGenerator);
30 | $resetPassword->addToken($user);
31 |
32 | $this->assertEquals('token-genereted', $user->getResetPasswordToken());
33 | $this->assertNotNull($user->getTokenExpirationDate());
34 | }
35 |
36 | private function getEntityManagerMock()
37 | {
38 | return $this
39 | ->getMockBuilder(EntityManagerInterface::class)
40 | ->disableOriginalConstructor()
41 | ->getMock();
42 | }
43 |
44 | private function getTokenGeneratorMock()
45 | {
46 | return $this
47 | ->getMockBuilder(TokenGeneratorInterface::class)
48 | ->disableOriginalConstructor()
49 | ->getMock();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tests/Unit/Services/UploaderTest.php:
--------------------------------------------------------------------------------
1 | uploader = new Uploader('%kernel.project_dir%/public/uploads/articles/coverages/');
20 | }
21 |
22 | public function testHasNotNewImage()
23 | {
24 | $image = $this->getImage();
25 | $this->assertFalse($this->uploader->hasNewImage($image));
26 | }
27 |
28 | public function testHasNewImage()
29 | {
30 | $image = $this->getImage();
31 | $image->setFile($this->mockedFile('test.png'));
32 | $this->assertTrue($this->uploader->hasNewImage($image));
33 | }
34 |
35 | public function testHasNoActiveImage()
36 | {
37 | $image = $this->getImage();
38 | $this->assertFalse($this->uploader->hasActiveImage($image));
39 | }
40 |
41 | public function testHasActiveImage()
42 | {
43 | $image = $this
44 | ->getMockBuilder(Image::class)
45 | ->disableOriginalConstructor()
46 | ->getMock();
47 | $image
48 | ->method('getId')
49 | ->willReturn(384);
50 |
51 | $this->assertTrue($this->uploader->hasActiveImage($image));
52 | }
53 |
54 | public function testDeletedImageIsChecked()
55 | {
56 | $image = $this->getImage();
57 | $image->setDeletedImage(true);
58 |
59 | $this->assertTrue($this->uploader->isDeleteImageChecked($image));
60 | }
61 |
62 | public function testDeletedImageIsNotChecked()
63 | {
64 | $image = $this->getImage();
65 | $image->setDeletedImage(false);
66 |
67 | $this->assertFalse($this->uploader->isDeleteImageChecked($image));
68 | }
69 |
70 | public function testUploadImage()
71 | {
72 | $uploadedFile = $this
73 | ->getMockBuilder(UploadedFile::class)
74 | ->disableOriginalConstructor()
75 | ->getMock();
76 | $uploadedFile
77 | ->expects($this->once())
78 | ->method('move');
79 |
80 | $this->uploader->uploadImage($uploadedFile, 'sweet_name');
81 | }
82 |
83 | private function mockedFile(string $fileName): UploadedFile
84 | {
85 | return new UploadedFile(__FILE__, $fileName);
86 | }
87 |
88 | private function getImage(): Image
89 | {
90 | return new Image();
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | bootEnv(dirname(__DIR__).'/.env');
11 | }
12 |
--------------------------------------------------------------------------------
/translations/emails.en.yaml:
--------------------------------------------------------------------------------
1 | reset_password:
2 | title: Reset password request
3 |
--------------------------------------------------------------------------------
/translations/emails.fr.yaml:
--------------------------------------------------------------------------------
1 | reset_password:
2 | title: Demande reinitialisation de mot de passe
3 |
--------------------------------------------------------------------------------
/translations/messages.en.yaml:
--------------------------------------------------------------------------------
1 | generic:
2 | or: or
3 | by: by
4 |
5 | navbar:
6 | login: Log in
7 | signin: Sign in
8 | profile: Profile
9 | logout: Logout
10 | dashboard: Dashboard
11 | notification:
12 | no-notification: No notification
13 | article_created: published a new article
14 |
15 | home:
16 | read_more: Read more
17 | no_article: No article :(
18 |
19 | article:
20 | comment: Comment
21 | comments: Comments
22 | not_authenticate: You must be login to leave a comment
23 | login: Log in
24 | signin: Sign in
25 | form:
26 | post_comment: Leave a comment
27 | comment_placeholder: Leave a public comment..
28 |
29 | login:
30 | login: Log in
31 | login_with: Log in with
32 | forgot_password: Forgot password ?
33 | not_registered: Not registered ?
34 | create_account: Create an account
35 | form:
36 | username: Username
37 | password: Password
38 | submitted: Log in...
39 | flashmessage_success: You are now log in
40 |
41 |
42 | "Username could not be found.": User or password could not be found
43 |
44 | signin:
45 | signin: Sign in
46 | signin_with: Sign in with
47 | form:
48 | username: Username
49 | email: Email
50 | password: Password
51 | password_placeholder: strong password
52 | repeat_password: Repeat it
53 | repeat_password_placeholder: once again
54 | submitted: Sign in...
55 | flashmessage_success: Your account has been created. You can now log in
56 |
57 |
58 | forgot_password:
59 | forgot_password: Forgot password
60 | instruction: Fill in the field with you email account to get a reset link
61 | send: Send email
62 | submitted: Send...
63 | flashmessage_success: An email has been sent to this email address
64 |
65 | reset_password:
66 | reset_password: Reset password
67 | form:
68 | new_password: New password
69 | new_password_placeholder: strong password
70 | repeat_password: Repeat the new password
71 | repeat_password_placeholder: once again
72 | submit: Save
73 | submitted: Save in progress...
74 | flashmessage_success: Password has been reseted !
75 | flashmessage_token_expired: Token is expired. Get a new password request
76 |
77 | profile:
78 | sidebar:
79 | profile: Profile
80 | security: Security
81 | my_profile: My profile
82 | edit_profile: Edit my profile
83 | change_password: Change my password
84 | last_comments: Last comments
85 | badges: My badges
86 |
87 | backoffice:
88 | sidebar:
89 | dashboard: DASHBOARD
90 | articles: ARTICLES
91 | dashboard:
92 | article: Article
93 | articles: Articles
94 | comment: Comment
95 | comments: Comments
96 | user: User
97 | users: Users
98 | articles:
99 | list:
100 | title: THE ARTICLES
101 | new_article: New article
102 | article_title: Title
103 | article_author: Author
104 | article_posted_at: Posted at
105 | article_comment: Comments
106 | articlr_action: Actions
107 | no_article: No article
108 | deleted_confirm: Do you want to delete this article ?
109 | add:
110 | title: ADD ARTICLE
111 | edit:
112 | title: EDIT ARTICLE
113 | cover_image: Cover image
114 | delete_cover_image: Delete image
115 | delete_cover_image_confirm: Do you want to delete this image ?
116 | no_cover_image: No cover image
117 | add_edit_form:
118 | actuality: Actuality
119 | article_title: Title
120 | article_content: Content
121 | article_tags: Tags
122 | article_tags_helper: Split your tags with a comma
123 | article_media: Media
124 | submit_button: Publish
125 | flashmessage_publish: The article has been successfully created
126 | flashmessage_edit: The article has been successfully edited
127 | flashmessage_deleted_article: The article has been successfully deleted
128 |
129 |
--------------------------------------------------------------------------------
/translations/validators.en.yaml:
--------------------------------------------------------------------------------
1 | signin:
2 | unique_username: username is already used
3 | required_username: username is required
4 | min_char_username: username must have 3 characters at least
5 | wrong_char_username: username has wrong characters
6 | unique_email: email is already used
7 | required_email: email is required
8 | wrong_format_email: email format must be valid
9 | required_password: password is required
10 | min_char_password: password must have 6 characters at least
11 | same_password: passwords must be the same
12 |
13 | forgot_password:
14 | email_required: email is required
15 | email_not_exist: email doesn't exist
16 |
17 | reset_password:
18 | same_password: passwords must be the same
19 |
20 | backoffice:
21 | article:
22 | title_required: title is required
23 | content_required: content is required
24 | image_extension: file is not a valid image
25 |
26 |
--------------------------------------------------------------------------------
/translations/validators.fr.yaml:
--------------------------------------------------------------------------------
1 | signin:
2 | unique_username: l'identifiant est déjà utilisé
3 | required_username: l'identifiant est obligatoire
4 | min_char_username: l'identifiant doit contenir 3 caractères minimum
5 | wrong_char_username: l'identifiant contient des caractères interdits
6 | unique_email: l'email est déjà utilisé
7 | required_email: l'email est obligatoire
8 | wrong_format_email: le format de l'email n'est pas valide
9 | required_password: Le mot de passe est obligatoire
10 | min_char_password: Le mot de passe doit contenir 6 caractères minimum
11 | same_password: les mots de passe doivent être identiques
12 |
13 | forgot_password:
14 | email_required: l'email est obligatoire
15 | email_not_exist: L'email renseigné n'est lié à aucun compte
16 |
17 | reset_password:
18 | same_password: Les mots de passe doivent être identiques
19 |
20 | backoffice:
21 | article:
22 | title_required: le titre est obligatoire
23 | content_required: le contenu est obligatoire
24 | image_extension: le fichier n'est pas une image valide
25 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var Encore = require('@symfony/webpack-encore');
2 |
3 | Encore
4 | // directory where compiled assets will be stored
5 | .setOutputPath('public/build/')
6 | // public path used by the web server to access the output path
7 | .setPublicPath('/build')
8 | // only needed for CDN's or sub-directory deploy
9 | //.setManifestKeyPrefix('build/')
10 |
11 | /*
12 | * ENTRY CONFIG
13 | *
14 | * Add 1 entry for each "page" of your app
15 | * (including one that's included on every page - e.g. "app")
16 | *
17 | * Each entry will result in one JavaScript file (e.g. app.js)
18 | * and one CSS file (e.g. app.css) if you JavaScript imports CSS.
19 | */
20 | .addEntry('/js/app', './assets/js/app.js')
21 | .addEntry('/js/delete-confirmation', './assets/js/delete-confirmation.js')
22 | .addEntry('/js/mercure-subscribe', './assets/js/mercure-subscribe.js')
23 | .addEntry('/js/tags', './assets/js/tags.js')
24 | .addStyleEntry('/css/app', ['./assets/scss/app.scss'])
25 |
26 | .enableBuildNotifications()
27 | .enableSassLoader()
28 | ;
29 |
30 | module.exports = Encore.getWebpackConfig();
31 |
--------------------------------------------------------------------------------