ちんこ

守矢諏訪子 <suwako@076.moe>、2026-11-11 15:24
コミット a764d87e34735bcddafa95308744e259130f2218 ・親 90fc153 ・ファイルを見る

2個のファイル、+50 -1

@@ -539,7 +539,7 @@
539539 'original' => self::safeUrl(is_string($o['url'] ?? null) ? $o['url'] : $id),
540540 'author' => $this->who($authorUrl),
541541 'booster' => ($e['type'] ?? '') === 'boost' ? $this->who($e['actor']) : null,
542- 'content' => Html::sanitize((string)($o['content'] ?? '')),
542+ 'content' => $this->localMentionLinks(Html::sanitize((string)($o['content'] ?? '')), $o),
543543 'summary' => (string)($o['summary'] ?? ''),
544544 'sensitive' => (bool)($o['sensitive'] ?? false) || ($o['summary'] ?? '') !== '',
545545 'attachments' => $attachments,
@@ -605,6 +605,38 @@
605605 ];
606606 }
607607
608+ /**
609+ * 本文のメンションのリンクを、相手のサーバーではなくこのサイトのプロフィールのページ(/microblog/remote等)にする
610+ * 投稿のtag(Mention)でアクターが分かるリンクだけ。ログインしていなければ他のサーバーに問い合わせないので、
611+ * キャッシュしているアクターだけ(それ以外は元のリンクのまま)
612+ *
613+ * @param string $html 安全にした本文
614+ * @param array $o 投稿(Note等)
615+ * @return string
616+ */
617+ private function localMentionLinks(string $html, array $o): string {
618+ $actors = [];
619+ foreach ((array)($o['tag'] ?? []) as $t) {
620+ if (!is_array($t) || ($t['type'] ?? '') !== 'Mention' || !is_string($t['href'] ?? null)) continue;
621+ $actors[$t['href']] = $t['href'];
622+ // Mastodon・Misskey等はリンクにプロフィールのURL(https://host/@user)を使うので、サーバーとユーザー名でも探す
623+ $name = strtolower(explode('@', ltrim((string)($t['name'] ?? ''), '@'))[0]);
624+ if ($name !== '') $actors[Fedi::domainOf($t['href']).'/'.$name] ??= $t['href'];
625+ }
626+ if (empty($actors)) return $html;
627+
628+ return preg_replace_callback('#<a href="([^"]*)" class="([^"]*)"#', function(array $m) use ($actors): string {
629+ $classes = explode(' ', $m[2]);
630+ if (!in_array('mention', $classes, true) || in_array('hashtag', $classes, true)) return $m[0];
631+ $href = html_entity_decode($m[1], ENT_QUOTES | ENT_HTML5, 'UTF-8');
632+ $user = strtolower(ltrim(basename((string)parse_url($href, PHP_URL_PATH)), '@'));
633+ $actor = $actors[$href] ?? $actors[Fedi::domainOf($href).'/'.$user] ?? null;
634+ if ($actor === null) return $m[0];
635+ if ($this->me === null && !Fedi::isLocal($actor) && Remote::cachedActor($actor) === null) return $m[0];
636+ return '<a href="'.Html::esc($this->actorPage($actor)).'" class="'.$m[2].'"';
637+ }, $html) ?? $html;
638+ }
639+
608640 /** ログイン中のユーザーとアクターの関係 */
609641 private function relation(string $url): array {
610642 if ($this->me === null) return ['self' => false, 'can' => false];
@@ -689,6 +721,8 @@
689721 $tmpl->assign('pagetit', $title);
690722 $tmpl->assign('curPage', 'microblog');
691723 $tmpl->assign('menu', $this->getMenu());
724+ $tmpl->assign('support', $this->getSupport());
725+ $tmpl->assign('sns', $this->getSns());
692726 $tmpl->assign('description', SITEINFO['description']);
693727 $tmpl->assign('user', $this->user);
694728 $tmpl->assign('me', $this->me);
@@ -106,4 +106,19 @@
106106
107107 return $menu;
108108 }
109+
110+ /**
111+ * フッターの支援のリンク(alt、class、img、href、show)
112+ * LittleBeastのフッターでは使わないが、サイトのフッターで表示する時の為に全部のページで割り当てる
113+ */
114+ public function getSupport(): array {
115+ return [];
116+ }
117+
118+ /**
119+ * フッターのSNSのリンク(getSupport()と同じ形)
120+ */
121+ public function getSns(): array {
122+ return [];
123+ }
109124 }