Tag.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. <?php
  2. declare(strict_types = 1);
  3. namespace Component\Tag\Controller;
  4. use App\Core\Cache;
  5. use App\Core\Controller;
  6. use App\Core\DB\DB;
  7. use function App\Core\I18n\_m;
  8. use App\Entity as E;
  9. use App\Util\Common;
  10. use App\Util\Exception\ClientException;
  11. use App\Util\Exception\RedirectException;
  12. use App\Util\Formatting;
  13. use Component\Tag\Form\SelfTagsForm;
  14. use Component\Tag\Tag as CompTag;
  15. use Symfony\Component\Form\SubmitButton;
  16. use Symfony\Component\HttpFoundation\Request;
  17. class Tag extends Controller
  18. {
  19. private function process(string|array $canon_single_or_multi, null|string|array $tag_single_or_multi, string $key, string $query, string $template)
  20. {
  21. $actor = Common::actor();
  22. $page = $this->int('page') ?: 1;
  23. $lang = $this->string('lang');
  24. $results = Cache::pagedStream(
  25. key: $key,
  26. query: $query,
  27. query_args: ['canon' => $canon_single_or_multi],
  28. actor: $actor,
  29. page: $page,
  30. );
  31. return [
  32. '_template' => $template,
  33. 'tag_name' => $tag_single_or_multi,
  34. 'results' => $results,
  35. 'page' => $page,
  36. ];
  37. }
  38. public function single_note_tag(string $canon)
  39. {
  40. return $this->process(
  41. canon_single_or_multi: $canon,
  42. tag_single_or_multi: $this->string('tag'),
  43. key: CompTag::cacheKeys($canon)['note_single'],
  44. query: 'select n from note n join note_tag nt with n.id = nt.note_id where nt.canonical = :canon order by nt.created DESC, nt.note_id DESC',
  45. template: 'note_tag_feed.html.twig',
  46. );
  47. }
  48. public function multi_note_tags(string $canons)
  49. {
  50. return $this->process(
  51. canon_single_or_multi: explode(',', $canons),
  52. tag_single_or_multi: !\is_null($this->string('tags')) ? explode(',', $this->string('tags')) : null,
  53. key: CompTag::cacheKeys(str_replace(',', '-', $canons))['note_multi'],
  54. query: 'select n from note n join note_tag nt with n.id = nt.note_id where nt.canonical in (:canon) order by nt.created DESC, nt.note_id DESC',
  55. template: 'note_tag_feed.html.twig',
  56. );
  57. }
  58. public function single_actor_tag(string $canon)
  59. {
  60. return $this->process(
  61. canon_single_or_multi: $canon,
  62. tag_single_or_multi: $this->string('tag'),
  63. key: CompTag::cacheKeys($canon)['actor_single'],
  64. query: 'select a from actor a join actor_tag at with a.id = at.tagged where at.canonical = :canon order by at.modified DESC',
  65. template: 'actor_tag_feed.html.twig',
  66. );
  67. }
  68. public function multi_actor_tag(string $canons)
  69. {
  70. return $this->process(
  71. canon_single_or_multi: explode(',', $canons),
  72. tag_single_or_multi: !\is_null($this->string('tags')) ? explode(',', $this->string('tags')) : null,
  73. key: CompTag::cacheKeys(str_replace(',', '-', $canons))['actor_multi'],
  74. query: 'select a from actor a join actor_tag at with a.id = at.tagged where at.canonical = :canon order by at.modified DESC',
  75. template: 'actor_tag_feed.html.twig',
  76. );
  77. }
  78. /**
  79. * Generic settings page for an Actor's self tags
  80. */
  81. public static function settingsSelfTags(Request $request, E\Actor $target, string $details_id)
  82. {
  83. $actor = Common::actor();
  84. if (!$actor->canAdmin($target)) {
  85. throw new ClientException(_m('You don\'t have enough permissions to edit {nickname}\'s settings', ['{nickname}' => $target->getNickname()]));
  86. }
  87. $actor_tags = $target->getSelfTags();
  88. [$add_form, $existing_form] = SelfTagsForm::handleTags(
  89. $request,
  90. $actor_tags,
  91. handle_new: /**
  92. * Handle adding tags
  93. */
  94. function ($form) use ($request, $target, $details_id) {
  95. $data = $form->getData();
  96. $tags = $data['new-tags'];
  97. $language = $target->getTopLanguage()->getLocale();
  98. foreach ($tags as $tag) {
  99. $tag = CompTag::ensureValid($tag);
  100. [$at, ] = E\ActorTag::createOrUpdate([
  101. 'tagger' => $target->getId(),
  102. 'tagged' => $target->getId(),
  103. 'tag' => $tag,
  104. 'canonical' => CompTag::canonicalTag($tag, language: $language),
  105. 'use_canonical' => $data['new-tags-use-canon'],
  106. ]);
  107. DB::persist($at);
  108. }
  109. DB::flush();
  110. Cache::delete(E\Actor::cacheKeys($target->getId(), $target->getId())['tags']);
  111. throw new RedirectException($request->get('_route'), ['nickname' => $target->getNickname(), 'open' => $details_id]);
  112. },
  113. handle_existing: /**
  114. * Handle changes to the existing tags
  115. */
  116. function ($form, array $form_definition) use ($request, $target, $details_id) {
  117. $data = $form->getData();
  118. $changed = false;
  119. foreach (array_chunk($form_definition, 3) as $entry) {
  120. $tag = Formatting::removePrefix($entry[0][2]['data'], '#');
  121. $use_canon = $entry[1][2]['attr']['data'];
  122. /** @var SubmitButton $remove */
  123. $remove = $form->get($entry[2][0]);
  124. if ($remove->isClicked()) {
  125. $changed = true;
  126. DB::removeBy(
  127. 'actor_tag',
  128. [
  129. 'tagger' => $target->getId(),
  130. 'tagged' => $target->getId(),
  131. 'tag' => $tag,
  132. 'use_canonical' => $use_canon,
  133. ],
  134. );
  135. }
  136. /** @var SubmitButton $toggle_canon */
  137. $toggle_canon = $form->get($entry[1][0]);
  138. if ($toggle_canon->isSubmitted()) {
  139. $changed = true;
  140. $at = DB::find(
  141. 'actor_tag',
  142. [
  143. 'tagger' => $target->getId(),
  144. 'tagged' => $target->getId(),
  145. 'tag' => $tag,
  146. 'use_canonical' => $use_canon,
  147. ],
  148. );
  149. DB::persist($at->setUseCanonical(!$use_canon));
  150. }
  151. }
  152. if ($changed) {
  153. DB::flush();
  154. Cache::delete(E\Actor::cacheKeys($target->getId(), $target->getId())['tags']);
  155. throw new RedirectException($request->get('_route'), ['nickname' => $target->getNickname(), 'open' => $details_id]);
  156. }
  157. },
  158. remove_label: _m('Remove self tag'),
  159. add_label: _m('Add self tag'),
  160. );
  161. return [
  162. '_template' => 'self_tags_settings.fragment.html.twig',
  163. 'add_self_tags_form' => $add_form->createView(),
  164. 'existing_self_tags_form' => $existing_form?->createView(),
  165. ];
  166. }
  167. }