Conversation.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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. /**
  20. * @author Hugo Sales <hugo@hsal.es>
  21. * @author Eliseu Amaro <mail@eliseuama.ro>
  22. * @copyright 2021-2022 Free Software Foundation, Inc http://www.fsf.org
  23. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  24. */
  25. namespace Component\Conversation\Controller;
  26. use App\Core\Cache;
  27. use App\Core\DB\DB;
  28. use App\Core\Form;
  29. use function App\Core\I18n\_m;
  30. use App\Core\Log;
  31. use App\Core\Router\Router;
  32. use App\Entity\Note;
  33. use App\Util\Common;
  34. use App\Util\Exception\ClientException;
  35. use App\Util\Exception\NoLoggedInUser;
  36. use App\Util\Exception\NoSuchNoteException;
  37. use App\Util\Exception\RedirectException;
  38. use App\Util\Exception\ServerException;
  39. use Component\Collection\Util\Controller\FeedController;
  40. use Component\Conversation\Entity\ConversationMute;
  41. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  42. use Symfony\Component\HttpFoundation\Request;
  43. class Conversation extends FeedController
  44. {
  45. /**
  46. * Render conversation page.
  47. *
  48. * @param int $conversation_id To identify what Conversation is to be rendered
  49. *
  50. * @throws \App\Util\Exception\ServerException
  51. *
  52. * @return array Array containing keys: 'notes' (all known notes in the given Conversation), 'should_format' (boolean, stating if onFormatNoteList events may or not format given notes), 'page_title' (used as the title header)
  53. */
  54. public function showConversation(Request $request, int $conversation_id): array
  55. {
  56. return [
  57. '_template' => 'collection/notes.html.twig',
  58. 'notes' => $this->query(query: "note-conversation:{$conversation_id}")['notes'] ?? [],
  59. 'should_format' => false,
  60. 'page_title' => _m('Conversation'),
  61. ];
  62. }
  63. /**
  64. * Controller for the note reply non-JS page
  65. *
  66. * Leverages the `PostingModifyData` event to add the `reply_to_id` field from the GET variable 'reply_to_id'
  67. *
  68. * @throws ClientException
  69. * @throws NoLoggedInUser
  70. * @throws NoSuchNoteException
  71. * @throws ServerException
  72. *
  73. * @return array
  74. */
  75. public function addReply(Request $request)
  76. {
  77. $user = Common::ensureLoggedIn();
  78. $note_id = $this->int('reply_to_id', new ClientException(_m('Malformed query.')));
  79. $note = Note::ensureCanInteract(Note::getByPK($note_id), $user);
  80. $conversation_id = $note->getConversationId();
  81. return $this->showConversation($request, $conversation_id);
  82. }
  83. /**
  84. * Creates form view for Muting Conversation extra action.
  85. *
  86. * @param int $conversation_id The Conversation id that this action targets
  87. *
  88. * @throws \App\Util\Exception\NoLoggedInUser
  89. * @throws \App\Util\Exception\RedirectException
  90. * @throws \App\Util\Exception\ServerException
  91. *
  92. * @return array Array containing templating where the form is to be rendered, and the form itself
  93. */
  94. public function muteConversation(Request $request, int $conversation_id)
  95. {
  96. $user = Common::ensureLoggedIn();
  97. $is_muted = ConversationMute::isMuted($conversation_id, $user);
  98. $form = Form::create([
  99. ['mute_conversation', SubmitType::class, ['label' => $is_muted ? _m('Unmute') : _m('Mute'), 'attr' => ['class' => '']]],
  100. ]);
  101. $form->handleRequest($request);
  102. if ($form->isSubmitted() && $form->isValid()) {
  103. if (!$is_muted) {
  104. DB::persist(ConversationMute::create(['conversation_id' => $conversation_id, 'actor_id' => $user->getId()]));
  105. } else {
  106. DB::removeBy('conversation_mute', ['conversation_id' => $conversation_id, 'actor_id' => $user->getId()]);
  107. }
  108. DB::flush();
  109. Cache::delete(ConversationMute::cacheKeys($conversation_id, $user->getId())['mute']);
  110. // Redirect user to where they came from
  111. // Prevent open redirect
  112. if (!\is_null($from = $this->string('from'))) {
  113. if (Router::isAbsolute($from)) {
  114. Log::warning("Actor {$user->getId()} attempted to mute conversation {$conversation_id} and then get redirected to another host, or the URL was invalid ({$from})");
  115. throw new ClientException(_m('Can not redirect to outside the website from here'), 400); // 400 Bad request (deceptive)
  116. } else {
  117. // TODO anchor on element id
  118. throw new RedirectException(url: $from);
  119. }
  120. } else {
  121. // If we don't have a URL to return to, go to the instance root
  122. throw new RedirectException('root');
  123. }
  124. }
  125. return [
  126. '_template' => 'conversation/mute.html.twig',
  127. 'notes' => $this->query(query: "note-conversation:{$conversation_id}")['notes'] ?? [],
  128. 'is_muted' => $is_muted,
  129. 'form' => $form->createView(),
  130. ];
  131. }
  132. }