Notification.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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\Notification;
  20. use App\Core\DB\DB;
  21. use App\Core\Event;
  22. use function App\Core\I18n\_m;
  23. use App\Core\Log;
  24. use App\Core\Modules\Component;
  25. use App\Core\Router\RouteLoader;
  26. use App\Core\Router\Router;
  27. use App\Entity\Activity;
  28. use App\Entity\Actor;
  29. use App\Entity\LocalUser;
  30. use Component\FreeNetwork\FreeNetwork;
  31. use Component\Group\Entity\GroupInbox;
  32. use Component\Notification\Controller\Feed;
  33. class Notification extends Component
  34. {
  35. public function onAddRoute(RouteLoader $m): bool
  36. {
  37. $m->connect('feed_notifications', '/feed/notifications', [Feed::class, 'notifications']);
  38. return Event::next;
  39. }
  40. public function onCreateDefaultFeeds(int $actor_id, LocalUser $user, int &$ordering)
  41. {
  42. DB::persist(\App\Entity\Feed::create([
  43. 'actor_id' => $actor_id,
  44. 'url' => Router::url($route = 'feed_notifications'),
  45. 'route' => $route,
  46. 'title' => _m('Notifications'),
  47. 'ordering' => $ordering++,
  48. ]));
  49. return Event::next;
  50. }
  51. /**
  52. * Enqueues a notification for an Actor (user or group) which means
  53. * it shows up in their home feed and such.
  54. */
  55. public function onNewNotification(Actor $sender, Activity $activity, array $ids_already_known = [], ?string $reason = null): bool
  56. {
  57. $targets = $activity->getNotificationTargets(ids_already_known: $ids_already_known, sender_id: $sender->getId());
  58. $this->notify($sender, $activity, $targets, $reason);
  59. return Event::next;
  60. }
  61. /**
  62. * Bring given Activity to Targets's attention
  63. */
  64. public function notify(Actor $sender, Activity $activity, array $targets, ?string $reason = null): bool
  65. {
  66. $remote_targets = [];
  67. foreach ($targets as $target) {
  68. if ($target->getIsLocal()) {
  69. if ($target->isGroup()) {
  70. // FIXME: Make sure we check (for both local and remote) users are in the groups they send to!
  71. DB::persist(GroupInbox::create([
  72. 'group_id' => $target->getId(),
  73. 'activity_id' => $activity->getId(),
  74. ]));
  75. } else {
  76. if ($target->hasBlocked($activity->getActor())) {
  77. Log::info("Not saving reply to actor {$target->getId()} from sender {$sender->getId()} because of a block.");
  78. continue;
  79. }
  80. }
  81. if (Event::handle('NewNotificationShould', [$activity, $target]) === Event::next) {
  82. // TODO: use https://symfony.com/doc/current/notifier.html
  83. // XXX: Unideal as in failures the rollback will leave behind a false notification,
  84. // but most notifications (all) require flushing the objects first
  85. // Should be okay as long as implementors bear this in mind
  86. DB::wrapInTransaction(fn() => DB::persist(Entity\Notification::create([
  87. 'activity_id' => $activity->getId(),
  88. 'target_id' => $target->getId(),
  89. 'reason' => $reason,
  90. ])));
  91. }
  92. } else {
  93. // We have no authority nor responsibility of notifying remote actors of a remote actor's doing
  94. if ($sender->getIsLocal()) {
  95. $remote_targets[] = $target;
  96. }
  97. }
  98. }
  99. FreeNetwork::notify($sender, $activity, $remote_targets, $reason);
  100. return Event::next;
  101. }
  102. }