EditFeeds.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. <?php
  2. declare(strict_types = 1);
  3. // {{{ License
  4. // This file is part of GNU social - https://www.gnu.org/software/social
  5. //
  6. // GNU social is free software: you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // GNU social is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with GNU social. If not, see <http://www.gnu.org/licenses/>.
  18. // }}}
  19. namespace Component\LeftPanel\Controller;
  20. use App\Core\Cache;
  21. use App\Core\Controller;
  22. use App\Core\DB;
  23. use App\Core\Form;
  24. use function App\Core\I18n\_m;
  25. use App\Core\Router;
  26. use App\Entity\Feed;
  27. use App\Util\Common;
  28. use App\Util\Exception\ClientException;
  29. use App\Util\Exception\RedirectException;
  30. use Component\Collection\Util\Controller\FeedController;
  31. use Functional as F;
  32. use Symfony\Component\Form\Extension\Core\Type\IntegerType;
  33. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  34. use Symfony\Component\Form\Extension\Core\Type\TextType;
  35. use Symfony\Component\Form\SubmitButton;
  36. use Symfony\Component\HttpFoundation\Request;
  37. use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  38. class EditFeeds extends Controller
  39. {
  40. /**
  41. * Controller for editing the list of feeds in the user's left
  42. * panel. Adds and removes `\App\Entity\Feed`s as appropriate
  43. */
  44. public function __invoke(Request $request)
  45. {
  46. $user = Common::ensureLoggedIn();
  47. $key = Feed::cacheKey($user);
  48. $feeds = Feed::getFeeds($user);
  49. $form_definitions = [];
  50. foreach ($feeds as $feed) {
  51. $md5 = md5($feed->getUrl());
  52. $form_definitions[] = [$md5 . '-url', TextType::class, ['data' => $feed->getUrl(), 'label' => _m('URL'), 'block_prefix' => 'row_url']];
  53. $form_definitions[] = [$md5 . '-order', IntegerType::class, ['data' => $feed->getOrdering(), 'label' => _m('Order'), 'block_prefix' => 'row_order']];
  54. $form_definitions[] = [$md5 . '-title', TextType::class, ['data' => $feed->getTitle(), 'label' => _m('Title'), 'block_prefix' => 'row_title']];
  55. $form_definitions[] = [$md5 . '-remove', SubmitType::class, ['label' => _m('Remove'), 'block_prefix' => 'row_remove']];
  56. }
  57. $form_definitions[] = ['url', TextType::class, ['label' => _m('New feed'), 'required' => false]];
  58. $form_definitions[] = ['order', IntegerType::class, ['label' => _m('Order'), 'data' => (\count($form_definitions) / 4) + 1]];
  59. $form_definitions[] = ['title', TextType::class, ['label' => _m('Title'), 'required' => false]];
  60. $form_definitions[] = ['add', SubmitType::class, ['label' => _m('Add')]];
  61. $form_definitions[] = ['update_exisiting', SubmitType::class, ['label' => _m('Update existing')]];
  62. $form_definitions[] = ['reset', SubmitType::class, ['label' => _m('Reset to default values')]];
  63. $form = Form::create($form_definitions);
  64. $form->handleRequest($request);
  65. if ($form->isSubmitted() && $form->isValid()) {
  66. array_pop($form_definitions);
  67. array_pop($form_definitions);
  68. array_pop($form_definitions);
  69. array_pop($form_definitions);
  70. array_pop($form_definitions);
  71. $data = $form->getData();
  72. /** @var SubmitButton $update_existing */
  73. $update_existing = $form->get('update_exisiting');
  74. if ($update_existing->isClicked()) {
  75. // Each feed has a URL, an order and a title
  76. $feeds_data = array_chunk($data, 3, preserve_keys: true);
  77. // The last three would be the new one
  78. array_pop($feeds_data);
  79. // Sort by the order
  80. usort($feeds_data, fn ($fd_l, $fd_r) => next($fd_l) <=> next($fd_r));
  81. // Make the order sequential
  82. $order = 1;
  83. foreach ($feeds_data as $i => $fd) {
  84. next($fd);
  85. $feeds_data[$i][key($fd)] = $order++;
  86. }
  87. // Update the fields in the corresponding feed
  88. foreach ($feeds_data as $fd) {
  89. $md5 = str_replace('-url', '', array_key_first($fd));
  90. $feed = F\first($feeds, fn ($f) => md5($f->getUrl()) === $md5);
  91. $feed->setUrl($fd[$md5 . '-url']);
  92. $feed->setOrdering($fd[$md5 . '-order']);
  93. $feed->setTitle($fd[$md5 . '-title']);
  94. }
  95. DB::flush();
  96. Cache::delete($key);
  97. throw new RedirectException();
  98. }
  99. // TODO fix orderings when removing
  100. // Remove feed
  101. foreach ($form_definitions as [$field, $type, $opts]) {
  102. if (str_ends_with($field, '-url')) {
  103. $remove_id = str_replace('-url', '-remove', $field);
  104. /** @var SubmitButton $remove_button */
  105. $remove_button = $form->get($remove_id);
  106. if ($remove_button->isClicked()) {
  107. DB::remove(DB::getReference('feed', ['actor_id' => $user->getId(), 'url' => $opts['data']]));
  108. DB::flush();
  109. Cache::delete($key);
  110. throw new RedirectException();
  111. }
  112. }
  113. }
  114. /** @var SubmitButton $reset_button */
  115. $reset_button = $form->get('reset');
  116. if ($reset_button->isClicked()) {
  117. F\map(DB::findBy('feed', ['actor_id' => $user->getId()]), fn ($f) => DB::remove($f));
  118. DB::flush();
  119. Cache::delete($key);
  120. Feed::createDefaultFeeds($user->getId(), $user);
  121. DB::flush();
  122. throw new RedirectException();
  123. }
  124. // Add feed
  125. try {
  126. $match = Router::match($data['url']);
  127. $route = $match['_route'];
  128. $controller = $match['_controller'];
  129. if (!is_subclass_of($controller, FeedController::class)) {
  130. throw new ClientException(_m('The page with url "{url}" is not a valid feed', ['{url}' => $data['url']]));
  131. }
  132. DB::persist(Feed::create([
  133. 'actor_id' => $user->getId(),
  134. 'url' => $data['url'],
  135. 'route' => $route,
  136. 'title' => $data['title'],
  137. 'ordering' => $data['order'],
  138. ]));
  139. DB::flush();
  140. Cache::delete($key);
  141. throw new RedirectException();
  142. } catch (ResourceNotFoundException) {
  143. throw new ClientException(_m('Invalid route with url "{url}"', ['{url}' => $data['url']]), code: 404);
  144. }
  145. }
  146. return [
  147. '_template' => 'left_panel/edit_feeds.html.twig',
  148. 'edit_feeds' => $form->createView(),
  149. ];
  150. }
  151. }