From 9dc3272d40a1734b5617b6dd7c8e1cede703ed47 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Thu, 27 Jul 2023 18:28:25 +0200 Subject: [PATCH 01/85] [wip] mastodon integration --- .env | 4 ++ composer.json | 1 + config/services.yaml | 11 ++++ src/Api/MastodonClient.php | 60 +++++++++++++++++++ src/Command/MastodonCommentsSyncCommand.php | 49 +++++++++++++++ src/Entity/Blog/Post.php | 30 ++++++++++ src/Entity/ExportQueue.php | 20 +++++++ src/Form/Blog/PostType.php | 13 ++++ src/Repository/ExportQueueRepository.php | 66 +++++++++++++++++++++ templates/blog/post_admin/_form.html.twig | 2 +- templates/blog/post_admin/_show.html.twig | 5 ++ 11 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 src/Api/MastodonClient.php create mode 100644 src/Command/MastodonCommentsSyncCommand.php create mode 100644 src/Entity/ExportQueue.php create mode 100644 src/Repository/ExportQueueRepository.php diff --git a/.env b/.env index 5b6040b..15ff816 100644 --- a/.env +++ b/.env @@ -35,3 +35,7 @@ MAILER_SENDER=example@localhost ASSET_BASE_URL=null UMAMI_URL=null +MASTODON_HOST= +MASTODON_CLIENT_ID= +MASTODON_CLIENT_SECRET= +MASTODON_CLIENT_ACCESS_TOKEN= diff --git a/composer.json b/composer.json index 6ac589f..794e5b8 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,7 @@ "php": ">=8.0.0", "beberlei/doctrineextensions": "^1.3", "friendsofsymfony/jsrouting-bundle": "^2.7", + "fundevogel/php-mastodon": "^0.6.0", "gregwar/captcha-bundle": "^2.2", "knplabs/knp-markdown-bundle": "^1.9", "knplabs/knp-menu-bundle": "^3.1", diff --git a/config/services.yaml b/config/services.yaml index 832f63b..2ff4933 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -4,6 +4,10 @@ # Put parameters here that don't need to change on each machine where the app is deployed # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration parameters: + mastodon_host: '%env(MASTODON_HOST)%' + mastodon_client_id: '%env(MASTODON_CLIENT_ID)%' + mastodon_client_secret: '%env(MASTODON_CLIENT_SECRET)%' + mastodon_client_access_token: '%env(MASTODON_CLIENT_ACCESS_TOKEN)%' services: # default configuration for services in *this* file @@ -47,6 +51,13 @@ services: resource: '../src/Controller/' tags: ['controller.service_arguments'] + App\Api\MastodonClient: + arguments: + $host: '%mastodon_host%' + $clientId: '%mastodon_client_id%' + $clientSecret: '%mastodon_client_secret%' + $clientAccessToken: '%mastodon_client_access_token%' + site.route_loader: class: App\Core\Router\SiteRouteLoader tags: [routing.loader] diff --git a/src/Api/MastodonClient.php b/src/Api/MastodonClient.php new file mode 100644 index 0000000..4ee07d8 --- /dev/null +++ b/src/Api/MastodonClient.php @@ -0,0 +1,60 @@ + + */ +class MastodonClient +{ + protected ?Client $client = null; + + public function __construct( + protected ?string $host, + protected ?string $clientId, + protected ?string $clientSecret, + protected ?string $clientAccessToken + ) { + } + + public function __call(string $name, array $arguments) + { + if (!$this->client) { + $this->createClient(); + } + + return call_user_func_array([$this->client, $name], $arguments); + } + + protected function createClient(): self + { + $this->client = new Client($this->host); + $this->client->accessToken = $this->clientAccessToken; + + return $this; + } + + public function extractId(string $url): string + { + return basename($url); + } + + public function getTree(string $postId, array &$tree = []): array + { + $tree['id'] = $postId; + $tree = array_merge($tree, $this->statuses()->get($postId)->data); + $tree = array_merge($tree, $this->statuses()->context($postId)->data); + + foreach ($tree['descendants'] as $key => $descendant) { + if ($descendant['in_reply_to_id'] === $postId) { + $descendants['descendants'][$key] = $this->getTree($descendant['id'], $descendants['descendants'][$key]); + } + } + + return $tree; + } +} diff --git a/src/Command/MastodonCommentsSyncCommand.php b/src/Command/MastodonCommentsSyncCommand.php new file mode 100644 index 0000000..ee5b0d4 --- /dev/null +++ b/src/Command/MastodonCommentsSyncCommand.php @@ -0,0 +1,49 @@ +addArgument('arg1', InputArgument::OPTIONAL, 'Argument description') + // ->addOption('option1', null, InputOption::VALUE_NONE, 'Option description') + // ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + foreach ($this->query->create()->where('.mastodonUrl is not null')->find() as $post) { + dump($this->client->getTree($this->client->extractId($post->getMastodonUrl()))); + } + + return Command::SUCCESS; + } +} diff --git a/src/Entity/Blog/Post.php b/src/Entity/Blog/Post.php index 505c4e4..8d6d1d5 100644 --- a/src/Entity/Blog/Post.php +++ b/src/Entity/Blog/Post.php @@ -90,6 +90,12 @@ class Post implements EntityInterface #[ORM\Column(type: 'array')] private $parameters = []; + #[ORM\Column(type: 'string', length: 255, nullable: true)] + private $mastodonUrl; + + #[ORM\Column(type: 'json')] + private $mastodonComments = []; + public function __construct() { $this->categories = new ArrayCollection(); @@ -475,4 +481,28 @@ class Post implements EntityInterface } )[0]['value'] ?? null; } + + public function getMastodonUrl(): ?string + { + return $this->mastodonUrl; + } + + public function setMastodonUrl(?string $mastodonUrl): self + { + $this->mastodonUrl = $mastodonUrl; + + return $this; + } + + public function getMastodonComments(): ?array + { + return $this->mastodonComments; + } + + public function setMastodonComments(array $mastodonComments): self + { + $this->mastodonComments = $mastodonComments; + + return $this; + } } diff --git a/src/Entity/ExportQueue.php b/src/Entity/ExportQueue.php new file mode 100644 index 0000000..efbd0d4 --- /dev/null +++ b/src/Entity/ExportQueue.php @@ -0,0 +1,20 @@ +id; + } +} diff --git a/src/Form/Blog/PostType.php b/src/Form/Blog/PostType.php index 5661886..f38bbd4 100644 --- a/src/Form/Blog/PostType.php +++ b/src/Form/Blog/PostType.php @@ -202,6 +202,19 @@ class PostType extends AbstractType ] ); + $builder->add( + 'mastodonUrl', + TextType::class, + [ + 'label' => 'URL Mastodon', + 'required' => false, + 'attr' => [ + ], + 'constraints' => [ + ], + ] + ); + $builder->add( 'quickUrl', TextType::class, diff --git a/src/Repository/ExportQueueRepository.php b/src/Repository/ExportQueueRepository.php new file mode 100644 index 0000000..4ef538d --- /dev/null +++ b/src/Repository/ExportQueueRepository.php @@ -0,0 +1,66 @@ + + * + * @method ExportQueue|null find($id, $lockMode = null, $lockVersion = null) + * @method ExportQueue|null findOneBy(array $criteria, array $orderBy = null) + * @method ExportQueue[] findAll() + * @method ExportQueue[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class ExportQueueRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, ExportQueue::class); + } + + public function add(ExportQueue $entity, bool $flush = false): void + { + $this->getEntityManager()->persist($entity); + + if ($flush) { + $this->getEntityManager()->flush(); + } + } + + public function remove(ExportQueue $entity, bool $flush = false): void + { + $this->getEntityManager()->remove($entity); + + if ($flush) { + $this->getEntityManager()->flush(); + } + } + +// /** +// * @return ExportQueue[] Returns an array of ExportQueue objects +// */ +// public function findByExampleField($value): array +// { +// return $this->createQueryBuilder('e') +// ->andWhere('e.exampleField = :val') +// ->setParameter('val', $value) +// ->orderBy('e.id', 'ASC') +// ->setMaxResults(10) +// ->getQuery() +// ->getResult() +// ; +// } + +// public function findOneBySomeField($value): ?ExportQueue +// { +// return $this->createQueryBuilder('e') +// ->andWhere('e.exampleField = :val') +// ->setParameter('val', $value) +// ->getQuery() +// ->getOneOrNullResult() +// ; +// } +} diff --git a/templates/blog/post_admin/_form.html.twig b/templates/blog/post_admin/_form.html.twig index 2983cd8..eadd08a 100644 --- a/templates/blog/post_admin/_form.html.twig +++ b/templates/blog/post_admin/_form.html.twig @@ -38,7 +38,7 @@ - {% for item in ['image', 'image2', 'parameters', 'status', 'contentFormat', 'publishedAt'] %} + {% for item in ['image', 'image2', 'parameters', 'status', 'contentFormat', 'publishedAt', 'mastodonUrl'] %}
{{ form_row(form[item]) }}
diff --git a/templates/blog/post_admin/_show.html.twig b/templates/blog/post_admin/_show.html.twig index f3dae48..fee41e7 100644 --- a/templates/blog/post_admin/_show.html.twig +++ b/templates/blog/post_admin/_show.html.twig @@ -125,6 +125,11 @@ {{ entity.publishedAt ? entity.publishedAt|date('d/m/Y H:i') : '-' }} +
  • + URL Mastodon + + {{ entity.mastodonUrl|default('-') }} +
  • From 70cef70bf495f9d2cff3beba4bf4ca07c0661a2c Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Thu, 27 Jul 2023 18:32:55 +0200 Subject: [PATCH 02/85] update murph --- src/Controller/UserAdminController.php | 79 +++++++++++++++++++ templates/_form.html.twig | 1 + templates/_show.html.twig | 1 + .../admin/user_admin/UserAdminController.php | 79 +++++++++++++++++++ templates/admin/user_admin/_form.html.twig | 1 + templates/admin/user_admin/_show.html.twig | 1 + 6 files changed, 162 insertions(+) create mode 100644 src/Controller/UserAdminController.php create mode 100644 templates/_form.html.twig create mode 100644 templates/_show.html.twig create mode 100644 templates/admin/user_admin/UserAdminController.php create mode 100644 templates/admin/user_admin/_form.html.twig create mode 100644 templates/admin/user_admin/_show.html.twig diff --git a/src/Controller/UserAdminController.php b/src/Controller/UserAdminController.php new file mode 100644 index 0000000..9970f51 --- /dev/null +++ b/src/Controller/UserAdminController.php @@ -0,0 +1,79 @@ + '\d+'])] + public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response + { + return parent::index($query, $request, $session, $page); + } + + #[Route(path: '/admin/user/new', name: 'admin_user_new', methods: ['GET', 'POST'])] + public function new(Factory $factory, EntityManager $entityManager, Request $request, TokenGenerator $tokenGenerator): Response + { + return parent::new($factory, $entityManager, $request, $tokenGenerator); + } + + #[Route(path: '/admin/user/show/{entity}', name: 'admin_user_show', methods: ['GET'])] + public function show(Entity $entity): Response + { + return parent::show($entity); + } + + #[Route(path: '/admin/user/filter', name: 'admin_user_filter', methods: ['GET'])] + public function filter(Session $session): Response + { + return parent::filter($session); + } + + #[Route(path: '/admin/user/edit/{entity}', name: 'admin_user_edit', methods: ['GET', 'POST'])] + public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response + { + return parent::edit($entity, $entityManager, $request); + } + + #[Route(path: '/admin/user/inline_edit/{entity}/{context}/{label}', name: 'admin_user_inline_edit', methods: ['GET', 'POST'])] + public function inlineEdit(string $context, string $label, Entity $entity, EntityManager $entityManager, Request $request): Response + { + return parent::inlineEdit($context, $label, $entity, $entityManager, $request); + } + + #[Route(path: '/admin/user/delete/{entity}', name: 'admin_user_delete', methods: ['DELETE', 'POST'])] + public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response + { + return parent::delete($entity, $entityManager, $request); + } + + #[Route(path: '/admin/user/resetting_request/{entity}', name: 'admin_user_resetting_request', methods: ['POST'])] + public function requestResetting(Entity $entity, EventDispatcherInterface $eventDispatcher, Request $request): Response + { + return parent::requestResetting($entity, $eventDispatcher, $request); + } + + protected function getConfiguration(): CrudConfiguration + { + if ($this->configuration) { + return $this->configuration; + } + + return parent::getConfiguration() + ->setView('form', 'admin/user_admin/_form.html.twig') + ->setView('show_entity', 'admin/user_admin/_show.html.twig') + ; + } +} diff --git a/templates/_form.html.twig b/templates/_form.html.twig new file mode 100644 index 0000000..ed04750 --- /dev/null +++ b/templates/_form.html.twig @@ -0,0 +1 @@ +{{ include('@Core/user/user_admin/_form.html.twig') }} diff --git a/templates/_show.html.twig b/templates/_show.html.twig new file mode 100644 index 0000000..806f07d --- /dev/null +++ b/templates/_show.html.twig @@ -0,0 +1 @@ +{{ include('@Core/user/user_admin/_show.html.twig') }} diff --git a/templates/admin/user_admin/UserAdminController.php b/templates/admin/user_admin/UserAdminController.php new file mode 100644 index 0000000..9970f51 --- /dev/null +++ b/templates/admin/user_admin/UserAdminController.php @@ -0,0 +1,79 @@ + '\d+'])] + public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response + { + return parent::index($query, $request, $session, $page); + } + + #[Route(path: '/admin/user/new', name: 'admin_user_new', methods: ['GET', 'POST'])] + public function new(Factory $factory, EntityManager $entityManager, Request $request, TokenGenerator $tokenGenerator): Response + { + return parent::new($factory, $entityManager, $request, $tokenGenerator); + } + + #[Route(path: '/admin/user/show/{entity}', name: 'admin_user_show', methods: ['GET'])] + public function show(Entity $entity): Response + { + return parent::show($entity); + } + + #[Route(path: '/admin/user/filter', name: 'admin_user_filter', methods: ['GET'])] + public function filter(Session $session): Response + { + return parent::filter($session); + } + + #[Route(path: '/admin/user/edit/{entity}', name: 'admin_user_edit', methods: ['GET', 'POST'])] + public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response + { + return parent::edit($entity, $entityManager, $request); + } + + #[Route(path: '/admin/user/inline_edit/{entity}/{context}/{label}', name: 'admin_user_inline_edit', methods: ['GET', 'POST'])] + public function inlineEdit(string $context, string $label, Entity $entity, EntityManager $entityManager, Request $request): Response + { + return parent::inlineEdit($context, $label, $entity, $entityManager, $request); + } + + #[Route(path: '/admin/user/delete/{entity}', name: 'admin_user_delete', methods: ['DELETE', 'POST'])] + public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response + { + return parent::delete($entity, $entityManager, $request); + } + + #[Route(path: '/admin/user/resetting_request/{entity}', name: 'admin_user_resetting_request', methods: ['POST'])] + public function requestResetting(Entity $entity, EventDispatcherInterface $eventDispatcher, Request $request): Response + { + return parent::requestResetting($entity, $eventDispatcher, $request); + } + + protected function getConfiguration(): CrudConfiguration + { + if ($this->configuration) { + return $this->configuration; + } + + return parent::getConfiguration() + ->setView('form', 'admin/user_admin/_form.html.twig') + ->setView('show_entity', 'admin/user_admin/_show.html.twig') + ; + } +} diff --git a/templates/admin/user_admin/_form.html.twig b/templates/admin/user_admin/_form.html.twig new file mode 100644 index 0000000..ed04750 --- /dev/null +++ b/templates/admin/user_admin/_form.html.twig @@ -0,0 +1 @@ +{{ include('@Core/user/user_admin/_form.html.twig') }} diff --git a/templates/admin/user_admin/_show.html.twig b/templates/admin/user_admin/_show.html.twig new file mode 100644 index 0000000..806f07d --- /dev/null +++ b/templates/admin/user_admin/_show.html.twig @@ -0,0 +1 @@ +{{ include('@Core/user/user_admin/_show.html.twig') }} From 53aa16951e08e55669d1d7e90f2f64fa296220e2 Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 30 Jul 2023 16:58:26 +0200 Subject: [PATCH 03/85] fix liip add constraints --- config/packages/liip_imagine.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/packages/liip_imagine.yaml b/config/packages/liip_imagine.yaml index e3cd592..157b0a7 100644 --- a/config/packages/liip_imagine.yaml +++ b/config/packages/liip_imagine.yaml @@ -13,18 +13,21 @@ liip_imagine: max: [600, 600] crop: size: [600, 270] + start: [0, 0] project_preview_filter: filters: downscale: max: [600, 600] crop: size: [600, 270] + start: [0, 0] post_preview_filter: filters: downscale: max: [600, 600] crop: size: [600, 300] + start: [0, 0] site_avatar: filters: downscale: From fccf54ed5a718837aabe15577e66181052129b4e Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Sun, 30 Jul 2023 16:58:31 +0200 Subject: [PATCH 04/85] fix liip add constraints --- src/Form/Blog/PostType.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Form/Blog/PostType.php b/src/Form/Blog/PostType.php index 5661886..fc312a4 100644 --- a/src/Form/Blog/PostType.php +++ b/src/Form/Blog/PostType.php @@ -172,6 +172,7 @@ class PostType extends AbstractType 'attr' => [ ], 'constraints' => [ + new Image(), ], ] ); From dedf105f063fbdf6f13c49df0e4faac67f65d3ea Mon Sep 17 00:00:00 2001 From: Simon Vieille Date: Tue, 15 Aug 2023 11:02:27 +0200 Subject: [PATCH 05/85] update footer setting type --- assets/css/app.scss | 23 +++++++++++++++++++ .../SettingEventSubscriber.php | 18 ++++++++++++++- templates/module/_navigation.html.twig | 2 +- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/assets/css/app.scss b/assets/css/app.scss index adb21dc..2b79812 100644 --- a/assets/css/app.scss +++ b/assets/css/app.scss @@ -1287,6 +1287,29 @@ $links: ( } } +// @keyframes avatarlogo { +// 0% { +// -webkit-mask: linear-gradient(135deg,#000c 40%,#000,#000c 60%) 100% 100%/250% 250%; +// } +// 40% { +// -webkit-mask-position: 0 0; +// } +// 90% { +// -webkit-mask: linear-gradient(135deg,#000c 40%,#000,#000c 60%) 100% 100%/250% 250%; +// } +// 100% { +// -webkit-mask: linear-gradient(135deg,#000c 40%,#000,#000c 60%) 100% 100%/250% 250%; +// } +// } +// +// .avatar-logo { +// img, svg { +// _animation-name: avatarlogo; +// animation-duration: 7s; +// animation-iteration-count: infinite; +// } +// } + @media (prefers-color-scheme: dark) { .bg-box { background: #343c53; diff --git a/src/EventSubscriber/SettingEventSubscriber.php b/src/EventSubscriber/SettingEventSubscriber.php index c80235f..164585d 100644 --- a/src/EventSubscriber/SettingEventSubscriber.php +++ b/src/EventSubscriber/SettingEventSubscriber.php @@ -9,6 +9,7 @@ use Symfony\Component\Form\Extension\Core\Type\EmailType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use App\Core\Form\FileManager\FilePickerType; +use App\Core\Form\Type\TinymceTextareaType; /** * class SettingEventSubscriber. @@ -93,7 +94,7 @@ class SettingEventSubscriber extends EventSubscriber ); } - if (in_array($entity->getCode(), ['blog_footer', 'post_author_description'])) { + if (in_array($entity->getCode(), ['post_author_description'])) { $event->setOption('view', 'large'); $builder->add( @@ -107,5 +108,20 @@ class SettingEventSubscriber extends EventSubscriber ] ); } + + if (in_array($entity->getCode(), ['blog_footer'])) { + $event->setOption('view', 'large'); + + $builder->add( + 'value', + TinymceTextareaType::class, + [ + 'label' => $entity->getLabel(), + 'attr' => [ + 'rows' => 20, + ], + ] + ); + } } } diff --git a/templates/module/_navigation.html.twig b/templates/module/_navigation.html.twig index 1ea0c86..45fb604 100644 --- a/templates/module/_navigation.html.twig +++ b/templates/module/_navigation.html.twig @@ -5,7 +5,7 @@