role !== Roles::BANNED; $isPaywall = $user !== NULL && ($user->role & Roles::SUBSCRIBER); $isStaff = $user !== NULL && ($user->role & (Roles::ADMIN | Roles::STAFF)); if (!is_dir($path)) return $posts; $files = glob($path.'/*.md'); foreach ($files as $file) { $content = file_get_contents($file); $parts = explode('----', $content, 2); if (count($parts) != 2) continue; $metadata = []; $meta = explode("\n", trim($parts[0])); foreach ($meta as $line) { $line = trim($line); if (empty($line)) continue; $colonPos = strpos($line, ':'); if ($colonPos === false) continue; $key = trim(substr($line, 0, $colonPos)); $value = trim(substr($line, $colonPos + 1)); $value = trim($value, '"\''); if ($key == 'category') { $metadata[$key] = array_map('trim', explode(',', $value)); } else { $metadata[$key] = $value; } } // コメントはプレビューに出さない $articleBody = trim(Markdown::stripComments($parts[1])); $preview = mb_substr(strip_tags($articleBody), 0, 50).'...'; $slug = basename($file, '.md'); if (isset($metadata['draft']) && !$isStaff) continue; $cannotPreview = (!$isMember && in_array('memberonly', $metadata['category'])) || ((!$isPaywall && !$isStaff) && in_array('subscriberonly', $metadata['category'])) || (!$isStaff && in_array('staffonly', $metadata['category'])); $posts[] = [ 'title' => $metadata['title'] ?? '', 'date' => $metadata['date'] ?? '', 'thumbnail' => $metadata['thumbnail'] ?? '', 'thumborient' => $metadata['thumborient'] ?? '', 'category' => $metadata['category'] ?? [], 'uuid' => $metadata['uuid'] ?? '', 'preview' => ($cannotPreview ? '未許可' : $preview), 'slug' => $slug, ]; } // 日付でソート(新しい順) usort($posts, function($a, $b) { return strtotime($b['date']) - strtotime($a['date']); }); return $posts; } /** * ActivityPubで配信するブログ(config.phpのBLOGSでfederateがtrue)の投稿 * * @return array 投稿の配列(「section」はブログのディレクトリ) */ public function federatedPosts(): array { $posts = []; foreach (blogs() as $name => $b) { if (!$b['federate']) continue; foreach ($this->getPosts("/{$name}/", null) as $p) $posts[] = $p + ['section' => $name]; } return $posts; } /** * 記事のコメント欄をテンプレートに割り当てる(COMMENTS_ENABLEDが無効なら何もしない) * * @param Template $tmpl テンプレート * @param string $section 記事のディレクトリ(「blog」「tutorial/linalg」等) * @param string $slug 記事 * @param \stdClass $meta 記事のメタデータ * @param \stdClass|null $user ログイン中のユーザー * @return void */ protected function assignComments(Template $tmpl, string $section, string $slug, \stdClass $meta, ?\stdClass $user): void { if (!Comments::enabled()) return; $thread = "{$section}/{$slug}"; // 連合するブログの記事には、Fediverseからも返信出来る $fedi = null; if (ACTIVITYPUB_ENABLED && (blogs()[$section]['federate'] ?? false)) { $post = ['uuid' => (string)($meta->uuid ?? ''), 'date' => (string)($meta->date ?? ''), 'category' => (array)($meta->category ?? []), 'slug' => $slug, 'section' => $section, 'title' => (string)$meta->title]; $fedi = (new Activitypub([$post]))->objectId($post); } $tmpl->assign('comments', [ 'thread' => $thread, 'list' => Comments::view($thread, $user), 'count' => Comments::count($thread), 'canPost' => Comments::canPost($user), 'maxLength' => Comments::MAX_LENGTH, 'fedi' => $fedi, 'actor' => $fedi !== null ? '@'.FEDIINFO['actor'].'@'.Fedi::host() : '', ]); // クッキーはテンプレートの出力の前に設定する if ($user !== null) $tmpl->assign('csrf', make_csrf_token()); $tmpl->addCss('comments'); } }