diff --git a/assets/app.js b/assets/app.js index b1ad6bd..0d6ed7c 100644 --- a/assets/app.js +++ b/assets/app.js @@ -42,6 +42,7 @@ import './styles/04-pages/landing.css'; import './styles/04-pages/admin.css'; import './styles/04-pages/analytics.css'; import './styles/04-pages/author-media.css'; +import './styles/04-pages/forum.css'; // 05 - Utilities (last for highest specificity) import './styles/05-utilities/utilities.css'; diff --git a/assets/styles/02-layout/layout.css b/assets/styles/02-layout/layout.css index 8e6b2a9..357dd9b 100644 --- a/assets/styles/02-layout/layout.css +++ b/assets/styles/02-layout/layout.css @@ -25,7 +25,9 @@ body { nav, aside { position: sticky; - top: 70px; + top: 80px; + max-height: calc(100vh - 80px); + overflow: auto; } nav { diff --git a/assets/styles/04-pages/forum.css b/assets/styles/04-pages/forum.css new file mode 100644 index 0000000..f10058f --- /dev/null +++ b/assets/styles/04-pages/forum.css @@ -0,0 +1,135 @@ +/* Forum Styles - Old School Forum Look */ + +.forum-nav { + list-style: none; + padding: 0; + margin: 0; +} + +.forum-nav li { + margin: 0.5rem 0; +} + +.forum-nav button { + cursor: pointer; + padding: 0.5rem; + font-weight: bold; + background: #e9e9e9; + border: 1px solid #ccc; + border-radius: 4px; + width: 100%; + text-align: left; +} + +.forum-nav button:hover { + background: #ddd; +} + +.forum-nav .content { + padding: 0.5rem; + border: 1px solid #ccc; + border-top: none; + background: #f9f9f9; +} + +.forum-nav .content ul { + list-style: none; + padding: 0; + margin: 0; +} + +.forum-nav .content ul li { + margin: 0.25rem 0; +} + +.forum-nav .content ul li a { + display: block; + padding: 0.25rem 0.5rem; + text-decoration: none; + color: #333; +} + +.forum-nav .content ul li a:hover { + background: #f0f0f0; +} + +.subcategories-grid { + display: flex; + flex-direction: column; + gap: 1rem; + margin: 1rem 0; +} + +.sub-card { + border: 1px solid var(--color-primary); + background: #fff; + padding: 1rem; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.sub-card h3 { + margin: 0 0 0.5rem 0; +} + +.sub-card div { + display: flex; + justify-content: space-between; + align-items: center; +} + +.sub-card .count { + align-self: flex-end; + flex-shrink: 0; +} + +.articles-list { + list-style: none; + padding: 0; +} + +.article-item { + border: 1px solid #ddd; + padding: 1rem; + margin: 1rem 0; + background: #fafafa; +} + +.article-item h3 { + margin: 0 0 0.5rem 0; +} + +.article-item p { + margin: 0.5rem 0; +} + +.article-item small { + color: #666; +} + +.nav-sub-card { + border: 1px solid #333; + background: #fff; + padding: 0.5rem; + margin: 0.5rem 0; + display: flex; + flex-direction: column; + box-shadow: 1px 1px 3px rgba(0,0,0,0.2); +} + +.nav-sub-card h4 { + margin: 0 0 0.25rem 0; + font-size: 1rem; +} + +.nav-sub-card .tags { + flex: 1; +} + +.nav-sub-card .count { + font-size: 1.2rem; + font-weight: bold; + color: #333; + align-self: flex-end; +} diff --git a/assets/styles/media-discovery.css b/assets/styles/media-discovery.css index c0a7b29..b7cad5b 100644 --- a/assets/styles/media-discovery.css +++ b/assets/styles/media-discovery.css @@ -1,22 +1,5 @@ /* Media Discovery Page Styles */ -.discover-header { - text-align: center; - padding: 2rem 1rem; - max-width: 800px; - margin: 0 auto; -} - -.discover-header h1 { - font-size: 2.5rem; - margin-bottom: 0.5rem; -} - -.discover-subtitle { - color: #666; - font-size: 1.1rem; -} - .error-message { background: #f8d7da; color: #721c24; diff --git a/src/Controller/ArticleController.php b/src/Controller/ArticleController.php index 4389353..882df4c 100644 --- a/src/Controller/ArticleController.php +++ b/src/Controller/ArticleController.php @@ -358,68 +358,5 @@ class ArticleController extends AbstractController return $data; } - #[Route('/topics', name: 'topics')] - public function topics( - Request $request, - #[Autowire(service: 'fos_elastica.finder.articles')] PaginatedFinderInterface $finder - ): Response { - $topics = [ - 'bitcoin-crypto' => [ - 'name' => 'Bitcoin & Crypto', - 'tags' => ['bitcoin', 'bitkoin', 'lightning', 'decentralization', 'freedom', 'privacy', 'sovereignty'] - ], - 'nostr' => [ - 'name' => 'Nostr', - 'tags' => ['nostr', 'grownostr'] - ], - 'christianity' => [ - 'name' => 'Christianity', - 'tags' => ['jesus', 'christian', 'christianity', 'bible', 'trustjesus', 'biblestr'] - ], - 'travel' => [ - 'name' => 'Travel', - 'tags' => ['travel', 'europe'] - ], - 'photography' => [ - 'name' => 'Photography', - 'tags' => ['photography'] - ], - 'ai' => [ - 'name' => 'AI', - 'tags' => ['ai'] - ], - 'philosophy' => [ - 'name' => 'Philosophy', - 'tags' => ['philosophy'] - ], - 'art' => [ - 'name' => 'Art', - 'tags' => ['art'] - ] - ]; - - $selectedTopic = $request->query->get('topic'); - $articles = []; - if ($selectedTopic && isset($topics[$selectedTopic])) { - $tags = $topics[$selectedTopic]['tags']; - $query = [ - 'size' => 10, - 'sort' => [['createdAt' => ['order' => 'desc']]], - 'query' => [ - 'terms' => [ - 'topics' => $tags - ] - ] - ]; - $results = $finder->find($query); - $articles = array_slice($results, 0, 10); - } - - return $this->render('pages/topics.html.twig', [ - 'topics' => $topics, - 'selectedTopic' => $selectedTopic, - 'articles' => $articles, - ]); - } } diff --git a/src/Controller/ForumController.php b/src/Controller/ForumController.php new file mode 100644 index 0000000..bd64a92 --- /dev/null +++ b/src/Controller/ForumController.php @@ -0,0 +1,256 @@ +get('forum.index.counts.v2', function (ItemInterface $item) use ($index) { + // $item->expiresAfter(30); // 30s is a nice compromise for “live enough” + $allTags = $this->flattenAllTags(ForumTopics::TOPICS); // ['tag' => true, ...] + $counts = $this->fetchTagCounts($index, array_keys($allTags)); // ['tag' => count] + + // return $this->hydrateCategoryCounts(self::TOPICS, $counts); + //}); + $categoriesWithCounts = $this->hydrateCategoryCounts(ForumTopics::TOPICS, $counts); + return $this->render('forum/index.html.twig', [ + 'topics' => $categoriesWithCounts, + ]); + } + + #[Route('/forum/topic/{key}', name: 'forum_topic')] + public function topic( + string $key, + #[Autowire(service: 'fos_elastica.finder.articles')] PaginatedFinderInterface $finder, + #[Autowire(service: 'fos_elastica.index.articles')] \Elastica\Index $index, + Request $request + ): Response { + // key format: "{category}-{subcategory}" + $key = strtolower(trim($key)); + [$cat, $sub] = array_pad(explode('-', $key, 2), 2, null); + + if (!$cat || !$sub || !isset(ForumTopics::TOPICS[$cat]['subcategories'][$sub])) { + throw $this->createNotFoundException('Topic not found'); + } + + $topic = ForumTopics::TOPICS[$cat]['subcategories'][$sub]; + + // Count each tag in this subcategory in one shot + $tags = array_map('strval', $topic['tags']); + $tagCounts = $this->fetchTagCounts($index, $tags); + + // Fetch articles for the topic + $bool = new BoolQuery(); + $bool->addFilter(new Terms('topics', $tags)); + + $query = new Query($bool); + $query->setSize(20); + $query->setSort(['createdAt' => ['order' => 'desc']]); + + /** @var Pagerfanta $pager */ + $pager = $finder->findPaginated($query); + $pager->setMaxPerPage(20); + $pager->setCurrentPage(max(1, (int) $request->query->get('page', 1))); + $articles = iterator_to_array($pager->getCurrentPageResults()); + + // (Optional) also show latest threads under this topic scope + $page = max(1, (int) $request->query->get('page', 1)); + $perPage = 20; + $threads = $this->fetchThreads($index, [$tags]); // OR scope: any tag in subcategory + $threadsPage = array_slice($threads, ($page-1)*$perPage, $perPage); + + return $this->render('forum/topic.html.twig', [ + 'categoryKey' => $cat, + 'subcategoryKey' => $sub, + 'topic' => [ + 'name' => $topic['name'], + 'tags' => $tags, + ], + 'tags' => $tagCounts, // ['tag' => count] + 'threads' => $threadsPage, + 'total' => count($threads), + 'page' => $page, + 'perPage' => $perPage, + 'topics' => $this->getHydratedTopics($index), + 'articles' => $articles + ]); + } + + #[Route('/forum/tag/{tag}', name: 'forum_tag')] + public function tag( + string $tag, + #[Autowire(service: 'fos_elastica.finder.articles')] PaginatedFinderInterface $finder, + #[Autowire(service: 'fos_elastica.index.articles')] \Elastica\Index $index, + Request $request + ): Response { + $tag = strtolower(trim($tag)); + + $bool = new BoolQuery(); + // Correct Term usage: + $bool->addFilter(new Term(['topics' => $tag])); + + $query = new Query($bool); + $query->setSize(20); + + $query->setSort(['createdAt' => ['order' => 'desc']]); + + /** @var Pagerfanta $pager */ + $pager = $finder->findPaginated($query); + $pager->setMaxPerPage(20); + $pager->setCurrentPage(max(1, (int) $request->query->get('page', 1))); + $articles = iterator_to_array($pager->getCurrentPageResults()); + + return $this->render('forum/tag.html.twig', [ + 'tag' => $tag, + 'articles' => $articles, + 'pager' => $pager, // expose if you want numbered pagination links + 'topics' => $this->getHydratedTopics($index), + ]); + } + + // ---------- Helpers ---------- + + /** + * Flatten all tags from the taxonomy into a unique set. + * @return array + */ + private function flattenAllTags(array $categories): array + { + $set = []; + foreach ($categories as $cat) { + foreach ($cat['subcategories'] as $sub) { + foreach ($sub['tags'] as $tag) { + $set[strtolower($tag)] = true; + } + } + } + return $set; + } + + /** + * Run one ES query that returns counts for each tag (OR scope per tag). + * Uses a Filters aggregation keyed by tag to avoid N queries. + * + * @param \Elastica\Index $index + * @param string[] $tags + * @return array + */ + private function fetchTagCounts(\Elastica\Index $index, array $tags): array + { + $tags = array_values(array_unique(array_map('strtolower', array_map('trim', $tags)))); + if (!$tags) return []; + + $q = new Query(new Query\MatchAll()); + $filters = new FiltersAgg('tag_counts'); + + foreach ($tags as $tag) { + $b = new BoolQuery(); + $b->addFilter(new Term(['topics' => $tag])); // topics must be keyword + lowercase normalizer + $filters->addFilter($b, $tag); + } + + $q->addAggregation($filters); + $q->setSize(0); + + $res = $index->search($q); + $agg = $res->getAggregation('tag_counts')['buckets'] ?? []; + + $out = []; + foreach ($tags as $tag) { + $out[$tag] = isset($agg[$tag]['doc_count']) ? (int) $agg[$tag]['doc_count'] : 0; + } + return $out; + } + + /** + * Rehydrate taxonomy with counts per subcategory (sum of its tags). + * @param array $counts + */ + private function hydrateCategoryCounts(array $taxonomy, array $counts): array + { + $out = []; + foreach ($taxonomy as $catKey => $cat) { + $subs = []; + foreach ($cat['subcategories'] as $subKey => $sub) { + $sum = 0; + foreach ($sub['tags'] as $tag) { + $sum += $counts[strtolower($tag)] ?? 0; + } + $subs[$subKey] = $sub + ['count' => $sum]; + } + $out[$catKey] = $cat; + $out[$catKey]['subcategories'] = $subs; + } + return $out; + } + + /** + * (Optional) Fetch latest threads for a given OR-scope of tag groups. + * You can replace this with your Finder if you want entity hydration. + * + * @param array> $tagGroups e.g. [ ['bitcoin','lightning'] ] + * @return array> + */ + private function fetchThreads(\Elastica\Index $index, array $tagGroups, int $size = 200): array + { + $bool = new BoolQuery(); + + // For a simple OR across tags: use Terms query on 'topics' + // If you pass multiple groups and want AND across groups, adapt here. + $flatTags = []; + foreach ($tagGroups as $g) { foreach ($g as $t) { $flatTags[] = strtolower($t); } } + $flatTags = array_values(array_unique($flatTags)); + + if ($flatTags) { + $bool->addFilter(new Terms('topics', $flatTags)); + } + + $q = (new Query($bool)) + ->setSize($size) + ->addSort(['createdAt' => ['order' => 'desc']]); + + $rs = $index->search($q); + + // Map raw sources you need (adjust to your mapping) + return array_map(static function (\Elastica\Result $hit) { + $s = $hit->getSource(); + return [ + 'id' => $s['id'] ?? $hit->getId(), + 'title' => $s['title'] ?? '(untitled)', + 'excerpt' => $s['excerpt'] ?? null, + 'topics' => $s['topics'] ?? [], + 'created_at' => $s['createdAt'] ?? null, + ]; + }, $rs->getResults()); + } + + private function getHydratedTopics(\Elastica\Index $index): array + { + $allTags = $this->flattenAllTags(ForumTopics::TOPICS); + $counts = $this->fetchTagCounts($index, array_keys($allTags)); + return $this->hydrateCategoryCounts(ForumTopics::TOPICS, $counts); + } +} diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php index d5edc3a..720eabf 100644 --- a/src/Controller/SearchController.php +++ b/src/Controller/SearchController.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Controller; +use App\Util\ForumTopics; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; @@ -13,6 +14,8 @@ class SearchController extends AbstractController #[Route('/search')] public function index(): Response { - return $this->render('pages/search.html.twig'); + return $this->render('pages/search.html.twig', [ + 'topics' => ForumTopics::TOPICS, + ]); } } diff --git a/src/Twig/Components/Atoms/ForumAside.php b/src/Twig/Components/Atoms/ForumAside.php new file mode 100644 index 0000000..57cb040 --- /dev/null +++ b/src/Twig/Components/Atoms/ForumAside.php @@ -0,0 +1,12 @@ + [ + 'name' => 'Lifestyle', + 'subcategories' => [ + 'travel' => [ + 'name' => 'Travel & Exploration', + 'tags' => ['travel', 'adventure', 'destinations', 'culture', 'photography'], + ], + 'health' => [ + 'name' => 'Health & Wellness', + 'tags' => ['health', 'fitness', 'wellness', 'nutrition', 'meditation', 'mental-health'], + ], + 'relationships' => [ + 'name' => 'Relationships & Family', + 'tags' => ['relationships', 'family', 'parenting', 'marriage', 'friendship', 'community'], + ], + 'christianity' => [ + 'name' => 'Christianity & Faith', + 'tags' => ['jesus', 'christian', 'bible', 'faith', 'spirituality', 'religion'], + ], + 'philosophy' => [ + 'name' => 'Philosophy & Ethics', + 'tags' => ['philosophy', 'ethics', 'existentialism', 'metaphysics', 'logic'], + ], + 'education' => [ + 'name' => 'Education & Learning', + 'tags' => ['education', 'learning', 'school', 'university', 'teaching', 'homeschool'], + ], + 'finance' => [ + 'name' => 'Personal Finance', + 'tags' => ['budgeting', 'saving', 'investing', 'debt', 'retirement', 'economy'], + ], + ], + ], + + // ─────────── TECH ─────────── + 'tech' => [ + 'name' => 'Tech', + 'subcategories' => [ + 'bitcoin' => [ + 'name' => 'Bitcoin & Sound Money', + 'tags' => ['bitcoin', 'lightning', 'decentralization', 'freedom', 'privacy', 'sovereignty'], + ], + 'ai' => [ + 'name' => 'Artificial Intelligence', + 'tags' => ['ai', 'machine-learning', 'llm', 'neural-networks', 'automation', 'robotics'], + ], + 'nostr' => [ + 'name' => 'Nostr & Decentralized Social', + 'tags' => ['nostr', 'fediverse', 'social', 'protocol', 'identity', 'nip'], + ], + 'software' => [ + 'name' => 'Software Development', + 'tags' => ['code', 'programming', 'development', 'open-source', 'python', 'php', 'javascript'], + ], + 'hardware' => [ + 'name' => 'Hardware & Gadgets', + 'tags' => ['hardware', 'devices', 'gadgets', 'controllers', 'iot', 'electronics'], + ], + 'cybersecurity' => [ + 'name' => 'Cybersecurity & Privacy', + 'tags' => ['security', 'privacy', 'encryption', 'hacking', 'infosec', 'vpn'], + ], + 'science' => [ + 'name' => 'Science & Innovation', + 'tags' => ['science', 'innovation', 'research', 'biology', 'physics', 'space', 'technology'], + ], + ], + ], + + // ─────────── ART & CULTURE ─────────── + 'art' => [ + 'name' => 'Art & Culture', + 'subcategories' => [ + 'photography' => [ + 'name' => 'Photography', + 'tags' => ['photography', 'photojournalism', 'portrait', 'street', 'nature'], + ], + 'music' => [ + 'name' => 'Music', + 'tags' => ['music', 'audio', 'sound', 'composition', 'performance', 'production'], + ], + 'writing' => [ + 'name' => 'Writing & Literature', + 'tags' => ['writing', 'literature', 'books', 'poetry', 'fiction', 'non-fiction'], + ], + 'film' => [ + 'name' => 'Film & Video', + 'tags' => ['film', 'video', 'cinema', 'documentary', 'animation', 'production'], + ], + 'design' => [ + 'name' => 'Design & Creativity', + 'tags' => ['design', 'art', 'creativity', 'ui', 'ux', 'graphic-design'], + ], + 'history' => [ + 'name' => 'History & Society', + 'tags' => ['history', 'society', 'politics', 'culture', 'anthropology', 'archaeology'], + ], + ], + ], + + // ─────────── BUSINESS ─────────── + 'business' => [ + 'name' => 'Business', + 'subcategories' => [ + 'entrepreneurship' => [ + 'name' => 'Entrepreneurship', + 'tags' => ['entrepreneurship', 'startup', 'business', 'innovation', 'leadership'], + ], + 'marketing' => [ + 'name' => 'Marketing & Sales', + 'tags' => ['marketing', 'sales', 'advertising', 'branding', 'customer', 'growth'], + ], + 'economics' => [ + 'name' => 'Economics & Finance', + 'tags' => ['economics', 'finance', 'markets', 'trading', 'policy', 'macro'], + ], + 'management' => [ + 'name' => 'Management & Strategy', + 'tags' => ['management', 'strategy', 'operations', 'productivity', 'leadership'], + ], + 'real-estate' => [ + 'name' => 'Real Estate', + 'tags' => ['real-estate', 'property', 'housing', 'investment', 'development'], + ], + ], + ], + + // ─────────── SPORTS ─────────── + 'sports' => [ + 'name' => 'Sports', + 'subcategories' => [ + 'fitness' => [ + 'name' => 'Fitness & Training', + 'tags' => ['fitness', 'training', 'exercise', 'health', 'athletics', 'performance'], + ], + 'outdoor' => [ + 'name' => 'Outdoor Activities', + 'tags' => ['outdoor', 'hiking', 'camping', 'climbing', 'adventure', 'nature'], + ], + 'team-sports' => [ + 'name' => 'Team Sports', + 'tags' => ['football', 'basketball', 'baseball', 'soccer', 'hockey', 'team'], + ], + 'combat' => [ + 'name' => 'Combat Sports', + 'tags' => ['mma', 'boxing', 'wrestling', 'martial-arts', 'combat', 'fighting'], + ], + 'esports' => [ + 'name' => 'Esports & Gaming', + 'tags' => ['esports', 'gaming', 'video-games', 'competition', 'streaming'], + ], + ], + ], + + // ─────────── NEWS & POLITICS ─────────── + 'news' => [ + 'name' => 'News & Politics', + 'subcategories' => [ + 'politics' => [ + 'name' => 'Politics & Government', + 'tags' => ['politics', 'government', 'policy', 'election', 'democracy', 'law'], + ], + 'world-news' => [ + 'name' => 'World News', + 'tags' => ['news', 'world', 'international', 'geopolitics', 'diplomacy'], + ], + 'us-news' => [ + 'name' => 'US News', + 'tags' => ['us', 'america', 'united-states', 'domestic', 'national'], + ], + 'activism' => [ + 'name' => 'Activism & Social Issues', + 'tags' => ['activism', 'social', 'justice', 'equality', 'rights', 'protest'], + ], + 'media' => [ + 'name' => 'Media & Journalism', + 'tags' => ['media', 'journalism', 'press', 'reporting', 'freedom', 'censorship'], + ], + ], + ], + ]; +} diff --git a/templates/components/Atoms/ForumAside.html.twig b/templates/components/Atoms/ForumAside.html.twig new file mode 100644 index 0000000..d885d35 --- /dev/null +++ b/templates/components/Atoms/ForumAside.html.twig @@ -0,0 +1,13 @@ +
+

Forum

+
+{% for catKey, category in topics %} +
+ {{ category.name }} +
    + {% for subKey, sub in category.subcategories %} +
  • {{ sub.name }}
  • + {% endfor %} +
+
+{% endfor %} diff --git a/templates/components/Atoms/PageHeading.html.twig b/templates/components/Atoms/PageHeading.html.twig new file mode 100644 index 0000000..9b71d87 --- /dev/null +++ b/templates/components/Atoms/PageHeading.html.twig @@ -0,0 +1,8 @@ +
+
+

{{ heading }}

+ {% if tagline %} +

{{ tagline }}

+ {% endif %} +
+
diff --git a/templates/forum/index.html.twig b/templates/forum/index.html.twig new file mode 100644 index 0000000..f4bcc13 --- /dev/null +++ b/templates/forum/index.html.twig @@ -0,0 +1,32 @@ +{% extends 'layout.html.twig' %} + +{% block body %} + + +{% for catKey, category in topics %} +
+
+

{{ category.name }}

+
+
+
+ {% for subKey, sub in category.subcategories %} +
+

{{ sub.name }}

+
+
+ {% for tag in sub.tags %} + {{ tag }} + {% endfor %} +
+
{{ sub.count|default(0) }}
+
+
+ {% endfor %} +
+{% endfor %} +{% endblock %} + +{% block aside %} + +{% endblock %} diff --git a/templates/forum/tag.html.twig b/templates/forum/tag.html.twig new file mode 100644 index 0000000..6f77a7b --- /dev/null +++ b/templates/forum/tag.html.twig @@ -0,0 +1,22 @@ +{% extends 'layout.html.twig' %} + +{% block body %} + + +
+ {% for article in articles %} +
+

{{ article.title }}

+

{{ article.summary|slice(0, 200) }}{% if article.summary|length > 200 %}...{% endif %}

+ Published: {{ article.createdAt|date('Y-m-d H:i') }} +
+ {% endfor %} +
+{% if articles is empty %} +

No articles found for this tag.

+{% endif %} +{% endblock %} + +{% block aside %} + +{% endblock %} diff --git a/templates/forum/topic.html.twig b/templates/forum/topic.html.twig new file mode 100644 index 0000000..2385550 --- /dev/null +++ b/templates/forum/topic.html.twig @@ -0,0 +1,22 @@ +{% extends 'layout.html.twig' %} + +{% block body %} + + +
+ {% for article in articles %} +
+

{{ article.title }}

+

{{ article.summary|slice(0, 200) }}{% if article.summary|length > 200 %}...{% endif %}

+ Published: {{ article.createdAt|date('Y-m-d H:i') }} +
+ {% endfor %} +
+ {% if articles is empty %} +

No articles found for this tag.

+ {% endif %} +{% endblock %} + +{% block aside %} + +{% endblock %} diff --git a/templates/pages/latest-articles.html.twig b/templates/pages/latest-articles.html.twig index 4ba2f35..1b7ca33 100644 --- a/templates/pages/latest-articles.html.twig +++ b/templates/pages/latest-articles.html.twig @@ -1,12 +1,7 @@ {% extends 'layout.html.twig' %} {% block body %} -
-
-

Latest Articles

-

Fresh off the presses

-
-
+
{% if articles is empty %} diff --git a/templates/pages/media-discovery.html.twig b/templates/pages/media-discovery.html.twig index f1a003f..593322a 100644 --- a/templates/pages/media-discovery.html.twig +++ b/templates/pages/media-discovery.html.twig @@ -6,11 +6,7 @@ {% endblock %} {% block body %} - -
-

Multimedia

-

Discovery through serendipity

-
+
{% if error is defined %} diff --git a/templates/pages/search.html.twig b/templates/pages/search.html.twig index e0d00b7..bc244f9 100644 --- a/templates/pages/search.html.twig +++ b/templates/pages/search.html.twig @@ -12,3 +12,7 @@
{% endblock %} + +{% block aside %} + +{% endblock %}