Favourite.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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 Plugin\Favourite\Controller;
  20. use App\Core\Controller\FeedController;
  21. use App\Core\DB\DB;
  22. use App\Core\Form;
  23. use App\Core\Log;
  24. use App\Core\Router\Router;
  25. use App\Util\Common;
  26. use App\Util\Exception\ClientException;
  27. use App\Util\Exception\InvalidFormException;
  28. use App\Util\Exception\NoLoggedInUser;
  29. use App\Util\Exception\NoSuchNoteException;
  30. use App\Util\Exception\RedirectException;
  31. use App\Util\Exception\ServerException;
  32. use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  33. use Symfony\Component\HttpFoundation\Request;
  34. use function App\Core\I18n\_m;
  35. use function is_null;
  36. class Favourite extends FeedController
  37. {
  38. /**
  39. * @throws ServerException
  40. * @throws InvalidFormException
  41. * @throws NoLoggedInUser
  42. * @throws NoSuchNoteException
  43. * @throws RedirectException
  44. */
  45. public function favouriteAddNote(Request $request, int $id): bool|array
  46. {
  47. $user = Common::ensureLoggedIn();
  48. $actor_id = $user->getId();
  49. $opts = ['id' => $id];
  50. $add_favourite_note = DB::find('note', $opts);
  51. if (is_null($add_favourite_note)) {
  52. throw new NoSuchNoteException();
  53. }
  54. $form_add_to_favourite = Form::create([
  55. ['add_favourite', SubmitType::class,
  56. [
  57. 'label' => _m('Favourite note!'),
  58. 'attr' => [
  59. 'title' => _m('Favourite this note!'),
  60. ],
  61. ],
  62. ],
  63. ]);
  64. $form_add_to_favourite->handleRequest($request);
  65. if ($form_add_to_favourite->isSubmitted()) {
  66. if (!is_null(\Plugin\Favourite\Favourite::favourNote(note_id: $id, actor_id: $actor_id))) {
  67. DB::flush();
  68. } else {
  69. throw new ClientException(_m('Note already favoured!'));
  70. }
  71. // Redirect user to where they came from
  72. // Prevent open redirect
  73. if (!is_null($from = $this->string('from'))) {
  74. if (Router::isAbsolute($from)) {
  75. Log::warning("Actor {$actor_id} attempted to reply to a note and then get redirected to another host, or the URL was invalid ({$from})");
  76. throw new ClientException(_m('Can not redirect to outside the website from here'), 400); // 400 Bad request (deceptive)
  77. } else {
  78. // TODO anchor on element id
  79. throw new RedirectException($from);
  80. }
  81. } else {
  82. // If we don't have a URL to return to, go to the instance root
  83. throw new RedirectException('root');
  84. }
  85. }
  86. return [
  87. '_template' => 'favourite/add_to_favourites.html.twig',
  88. 'note' => $add_favourite_note,
  89. 'add_favourite' => $form_add_to_favourite->createView(),
  90. ];
  91. }
  92. /**
  93. * @throws ServerException
  94. * @throws InvalidFormException
  95. * @throws NoLoggedInUser
  96. * @throws NoSuchNoteException
  97. * @throws RedirectException
  98. */
  99. public function favouriteRemoveNote(Request $request, int $id): array
  100. {
  101. $user = Common::ensureLoggedIn();
  102. $actor_id = $user->getId();
  103. $opts = ['id' => $id];
  104. $remove_favourite_note = DB::find('note', $opts);
  105. if (is_null($remove_favourite_note)) {
  106. throw new NoSuchNoteException();
  107. }
  108. $form_remove_favourite = Form::create([
  109. ['remove_favourite', SubmitType::class,
  110. [
  111. 'label' => _m('Remove favourite'),
  112. 'attr' => [
  113. 'title' => _m('Remove note from favourites.'),
  114. ],
  115. ],
  116. ],
  117. ]);
  118. $form_remove_favourite->handleRequest($request);
  119. if ($form_remove_favourite->isSubmitted()) {
  120. if (!is_null(\Plugin\Favourite\Favourite::unfavourNote(note_id: $id, actor_id: $actor_id))) {
  121. DB::flush();
  122. } else {
  123. throw new ClientException(_m('Note already unfavoured!'));
  124. }
  125. // Redirect user to where they came from
  126. // Prevent open redirect
  127. if (!is_null($from = $this->string('from'))) {
  128. if (Router::isAbsolute($from)) {
  129. Log::warning("Actor {$actor_id} attempted to reply to a note and then get redirected to another host, or the URL was invalid ({$from})");
  130. throw new ClientException(_m('Can not redirect to outside the website from here'), 400); // 400 Bad request (deceptive)
  131. } else {
  132. // TODO anchor on element id
  133. throw new RedirectException($from);
  134. }
  135. } else {
  136. // If we don't have a URL to return to, go to the instance root
  137. throw new RedirectException('root');
  138. }
  139. }
  140. $note = DB::find('note', ['id' => $id]);
  141. return [
  142. '_template' => 'favourite/remove_from_favourites.html.twig',
  143. 'note' => $note,
  144. 'remove_favourite' => $form_remove_favourite->createView(),
  145. ];
  146. }
  147. public function favouritesByActorId(Request $request, int $id)
  148. {
  149. $notes = DB::dql(
  150. <<< 'EOF'
  151. select n from note n
  152. join favourite f with n.id = f.note_id
  153. where f.actor_id = :id
  154. order by f.created DESC
  155. EOF,
  156. ['id' => $id],
  157. );
  158. return [
  159. '_template' => 'feeds/feed.html.twig',
  160. 'page_title' => 'Favourites feed.',
  161. 'notes' => $notes,
  162. ];
  163. }
  164. public function favouritesByActorNickname(Request $request, string $nickname)
  165. {
  166. $user = DB::findOneBy('local_user', ['nickname' => $nickname]);
  167. return self::favouritesByActorId($request, $user->getId());
  168. }
  169. /**
  170. * Reverse favourites stream
  171. *
  172. * @return array template
  173. * @throws NoLoggedInUser user not logged in
  174. *
  175. */
  176. public function reverseFavouritesByActorId(Request $request, int $id): array
  177. {
  178. $notes = DB::dql(
  179. <<< 'EOF'
  180. select n from note n
  181. join favourite f with n.id = f.note_id
  182. where f.actor_id != :id
  183. and n.actor_id = :id
  184. order by f.created DESC
  185. EOF,
  186. ['id' => $id],
  187. );
  188. return [
  189. '_template' => 'feeds/feed.html.twig',
  190. 'page_title' => 'Reverse favourites feed.',
  191. 'notes' => $notes,
  192. ];
  193. }
  194. public function reverseFavouritesByActorNickname(Request $request, string $nickname)
  195. {
  196. $user = DB::findOneBy('local_user', ['nickname' => $nickname]);
  197. return self::reverseFavouritesByActorId($request, $user->getId());
  198. }
  199. }