Filter future-dated posts from public views and add unfiltered post access
- all_posts/0 now excludes posts where date > today - all_tags/0 computed at runtime from filtered posts - posts_by_tag/1 and recent_posts/1 inherit date filtering - Add unfiltered_posts/0 to Blog macro and FakeBlog - Add all_posts_unfiltered/0 to Registry for dashboard use
This commit is contained in:
parent
037e9f86ff
commit
0577ceced0
@ -1,7 +1,7 @@
|
||||
{"id":"firehose-1h8","title":"Verify feeds exclude future-dated posts","description":"## Context\nRSS/Atom feeds call blog.all_posts() which should now filter by date (from Step 1).\nAdd explicit tests confirming feeds exclude future-dated published posts.\n\n## Scope\n- blogex/test/blogex/feed_test.exs\n\n## TDD\nRED: Test RSS and Atom feeds exclude future-dated published posts\nGREEN: Should already pass from Step 1 changes\nREFACTOR: None","status":"open","priority":2,"issue_type":"task","owner":"willem+gitea@livingsoftware.co.uk","created_at":"2026-04-01T20:07:16.213785081Z","created_by":"Willem van den Ende","updated_at":"2026-04-01T20:07:16.213785081Z","dependencies":[{"issue_id":"firehose-1h8","depends_on_id":"firehose-2wc","type":"blocks","created_at":"2026-04-01T20:07:52.701493058Z","created_by":"Willem van den Ende"}]}
|
||||
{"id":"firehose-1x3","title":"Make get_post/get_post! search all compiled posts (unfiltered)","description":"## Context\nget_post/1 and get_post!/1 currently search all_posts() (filtered). Change to search @posts (unfiltered)\nso direct URL access works for draft and scheduled posts. Enables preview links for reviewers.\n\n## Scope\n- blogex/lib/blogex/blog.ex: get_post/1, get_post!/1\n- blogex/test/support/fake_blog.ex: get_post/1, get_post!/1\n- blogex/test/blogex/blog_test.exs: update existing tests, add new ones\n\n## TDD\nRED: Test get_post! returns future-dated post, get_post returns draft post\nGREEN: Search @posts instead of all_posts()\nREFACTOR: Update existing test that expects get_post!(\"draft-post\") to raise","status":"open","priority":2,"issue_type":"task","owner":"willem+gitea@livingsoftware.co.uk","created_at":"2026-04-01T20:07:04.676875214Z","created_by":"Willem van den Ende","updated_at":"2026-04-01T20:07:04.676875214Z","dependencies":[{"issue_id":"firehose-1x3","depends_on_id":"firehose-2wc","type":"blocks","created_at":"2026-04-01T20:07:52.666577397Z","created_by":"Willem van den Ende"}]}
|
||||
{"id":"firehose-2wc","title":"Add date filtering to Blogex all_posts/0","description":"## Context\nall_posts() in blogex/lib/blogex/blog.ex (line 77-83) currently filters by `published` boolean only.\nAdd `date \u003c= Date.utc_today()` filter so future-dated posts are hidden from public views.\n\n## Scope\n- blogex/lib/blogex/blog.ex: all_posts/0\n- blogex/test/support/fake_blog.ex: all_posts/0\n- blogex/test/blogex/blog_test.exs: new tests\n- blogex/test/support/setup.ex: add future-dated post to default_posts\n\n## TDD\nRED: Test that future-dated published post is excluded from all_posts, posts_by_tag, recent_posts, all_tags\nGREEN: Add date filter after published filter\nREFACTOR: Extract filtering predicate if duplicated","status":"in_progress","priority":2,"issue_type":"task","owner":"willem+gitea@livingsoftware.co.uk","created_at":"2026-04-01T20:06:54.303723951Z","created_by":"Willem van den Ende","updated_at":"2026-04-01T20:15:39.395327878Z"}
|
||||
{"id":"firehose-4nq","title":"Add post visibility and days_until_live helpers","description":"## Context\nDashboard and status banners need to compute post visibility (draft/scheduled/live)\nand days until a scheduled post goes live.\n\n## Scope\n- blogex/lib/blogex/post.ex: add visibility/1 and days_until_live/1\n- blogex/test/blogex/post_test.exs: new tests\n\n## TDD\nRED: Test visibility returns :draft/:scheduled/:live correctly, days_until_live returns integer or nil\nGREEN: Implement functions\nREFACTOR: None","status":"in_progress","priority":2,"issue_type":"task","owner":"willem+gitea@livingsoftware.co.uk","created_at":"2026-04-01T20:07:44.5973142Z","created_by":"Willem van den Ende","updated_at":"2026-04-01T20:15:39.478922912Z"}
|
||||
{"id":"firehose-4nq","title":"Add post visibility and days_until_live helpers","description":"## Context\nDashboard and status banners need to compute post visibility (draft/scheduled/live)\nand days until a scheduled post goes live.\n\n## Scope\n- blogex/lib/blogex/post.ex: add visibility/1 and days_until_live/1\n- blogex/test/blogex/post_test.exs: new tests\n\n## TDD\nRED: Test visibility returns :draft/:scheduled/:live correctly, days_until_live returns integer or nil\nGREEN: Implement functions\nREFACTOR: None","status":"closed","priority":2,"issue_type":"task","owner":"willem+gitea@livingsoftware.co.uk","created_at":"2026-04-01T20:07:44.5973142Z","created_by":"Willem van den Ende","updated_at":"2026-04-01T20:24:39.851993851Z","closed_at":"2026-04-01T20:24:39.851993851Z","close_reason":"Closed"}
|
||||
{"id":"firehose-4yh","title":"Create LiveView editor dashboard","description":"## Context\nLiveView at /editor/dashboard behind auth. Two tabs: drafts and scheduled.\nUnified timeline across all blogs. Scheduled posts show \"X days until live\".\nLinks to post show page.\n\n## Scope\n- app/lib/firehose_web/live/editor_dashboard_live.ex\n- app/lib/firehose_web/router.ex: add /editor scope\n- app/test/firehose_web/live/editor_dashboard_live_test.exs\n\n## TDD\nRED: Unauth redirected, auth sees dashboard, drafts tab, scheduled tab with countdown, links work\nGREEN: Implement LiveView, add route\nREFACTOR: Extract tab component if markup duplicated","status":"open","priority":2,"issue_type":"task","owner":"willem+gitea@livingsoftware.co.uk","created_at":"2026-04-01T20:07:44.673871753Z","created_by":"Willem van den Ende","updated_at":"2026-04-01T20:07:44.673871753Z","dependencies":[{"issue_id":"firehose-4yh","depends_on_id":"firehose-4nq","type":"blocks","created_at":"2026-04-01T20:08:01.570736282Z","created_by":"Willem van den Ende"},{"issue_id":"firehose-4yh","depends_on_id":"firehose-ai8","type":"blocks","created_at":"2026-04-01T20:08:01.597663464Z","created_by":"Willem van den Ende"},{"issue_id":"firehose-4yh","depends_on_id":"firehose-dhh","type":"blocks","created_at":"2026-04-01T20:08:01.625180883Z","created_by":"Willem van den Ende"}]}
|
||||
{"id":"firehose-8zg","title":"Gate registration to ALLOWED_REGISTRATION_EMAIL","description":"## Context\nRegistration must be restricted to a single email from env var.\nUnset = disabled. Wrong email = \"registration is invite only.\"\n\n## Scope\n- app/config/runtime.exs: read ALLOWED_REGISTRATION_EMAIL\n- app/config/test.exs: set test value\n- Registration controller or Accounts context: add validation\n- Registration tests: add gating tests\n\n## TDD\nRED: Registration succeeds for matching email, fails for non-matching, fails when unset\nGREEN: Add config reading + validation check\nREFACTOR: None","status":"open","priority":2,"issue_type":"task","owner":"willem+gitea@livingsoftware.co.uk","created_at":"2026-04-01T20:07:28.051938506Z","created_by":"Willem van den Ende","updated_at":"2026-04-01T20:07:28.051938506Z","dependencies":[{"issue_id":"firehose-8zg","depends_on_id":"firehose-dhh","type":"blocks","created_at":"2026-04-01T20:08:01.502562336Z","created_by":"Willem van den Ende"}]}
|
||||
{"id":"firehose-ai8","title":"Add unfiltered post access for dashboard","description":"## Context\nDashboard needs access to all posts including drafts and future-dated.\nAdd unfiltered_posts/0 to Blog macro and all_posts_unfiltered/0 to Registry.\n\n## Scope\n- blogex/lib/blogex/blog.ex: add unfiltered_posts/0\n- blogex/lib/blogex/registry.ex: add all_posts_unfiltered/0\n- blogex/test/support/fake_blog.ex: add unfiltered_posts/0\n- blogex/test/blogex/registry_test.exs: new tests\n\n## TDD\nRED: Test unfiltered returns all posts including drafts and future-dated\nGREEN: Implement functions\nREFACTOR: None","status":"in_progress","priority":2,"issue_type":"task","owner":"willem+gitea@livingsoftware.co.uk","created_at":"2026-04-01T20:07:44.63593107Z","created_by":"Willem van den Ende","updated_at":"2026-04-01T20:15:39.519698002Z"}
|
||||
|
||||
@ -119,5 +119,6 @@ defmodule Blogex do
|
||||
defdelegate get_blog!(blog_id), to: Blogex.Registry
|
||||
defdelegate get_blog(blog_id), to: Blogex.Registry
|
||||
defdelegate all_posts, to: Blogex.Registry
|
||||
defdelegate all_posts_unfiltered, to: Blogex.Registry
|
||||
defdelegate all_tags, to: Blogex.Registry
|
||||
end
|
||||
|
||||
@ -73,12 +73,17 @@ defmodule Blogex.Blog do
|
||||
@doc "Returns the base URL path for this blog."
|
||||
def base_path, do: @blog_base_path
|
||||
|
||||
@doc "Returns all compiled posts regardless of published status or date."
|
||||
def unfiltered_posts, do: @posts
|
||||
|
||||
@doc "Returns all visible posts, newest first. Drafts are included in dev/test."
|
||||
def all_posts do
|
||||
today = Date.utc_today()
|
||||
|
||||
if Blogex.show_drafts?() do
|
||||
@posts
|
||||
Enum.filter(@posts, &(not Date.after?(&1.date, today)))
|
||||
else
|
||||
Enum.filter(@posts, & &1.published)
|
||||
Enum.filter(@posts, &(&1.published and not Date.after?(&1.date, today)))
|
||||
end
|
||||
end
|
||||
|
||||
@ -86,7 +91,12 @@ defmodule Blogex.Blog do
|
||||
def recent_posts(n \\ 5), do: Enum.take(all_posts(), n)
|
||||
|
||||
@doc "Returns all unique tags across all published posts."
|
||||
def all_tags, do: @tags
|
||||
def all_tags do
|
||||
all_posts()
|
||||
|> Enum.flat_map(& &1.tags)
|
||||
|> Enum.uniq()
|
||||
|> Enum.sort()
|
||||
end
|
||||
|
||||
@doc "Returns all published posts matching the given tag."
|
||||
def posts_by_tag(tag) do
|
||||
|
||||
@ -37,6 +37,13 @@ defmodule Blogex.Registry do
|
||||
|> Enum.sort_by(& &1.date, {:desc, Date})
|
||||
end
|
||||
|
||||
@doc "Returns all posts from all blogs (unfiltered), sorted newest first."
|
||||
def all_posts_unfiltered do
|
||||
blogs()
|
||||
|> Enum.flat_map(& &1.unfiltered_posts())
|
||||
|> Enum.sort_by(& &1.date, {:desc, Date})
|
||||
end
|
||||
|
||||
@doc "Returns all unique tags across all blogs."
|
||||
def all_tags do
|
||||
blogs()
|
||||
|
||||
@ -15,6 +15,24 @@ defmodule Blogex.BlogTest do
|
||||
assert "draft-post" not in ids
|
||||
end
|
||||
|
||||
test "excludes future-dated posts", %{blog: blog} do
|
||||
ids = blog.all_posts() |> Enum.map(& &1.id)
|
||||
|
||||
refute "future-post" in ids
|
||||
end
|
||||
|
||||
test "includes today-dated published posts" do
|
||||
{:ok, _} = FakeBlog.start([
|
||||
build(id: "today", date: Date.utc_today(), published: true),
|
||||
build(id: "tomorrow", date: Date.add(Date.utc_today(), 1), published: true)
|
||||
])
|
||||
|
||||
ids = FakeBlog.all_posts() |> Enum.map(& &1.id)
|
||||
|
||||
assert "today" in ids
|
||||
refute "tomorrow" in ids
|
||||
end
|
||||
|
||||
test "returns posts newest first", %{blog: blog} do
|
||||
dates = blog.all_posts() |> Enum.map(& &1.date)
|
||||
|
||||
@ -23,6 +41,13 @@ defmodule Blogex.BlogTest do
|
||||
end
|
||||
|
||||
describe "recent_posts/1" do
|
||||
test "excludes future-dated posts", %{blog: blog} do
|
||||
posts = blog.recent_posts(100)
|
||||
ids = Enum.map(posts, & &1.id)
|
||||
|
||||
refute "future-post" in ids
|
||||
end
|
||||
|
||||
test "returns at most n posts", %{blog: blog} do
|
||||
assert length(blog.recent_posts(2)) == 2
|
||||
end
|
||||
@ -35,6 +60,12 @@ defmodule Blogex.BlogTest do
|
||||
end
|
||||
|
||||
describe "posts_by_tag/1" do
|
||||
test "excludes future-dated posts", %{blog: blog} do
|
||||
posts = blog.posts_by_tag("future-only")
|
||||
|
||||
assert posts == []
|
||||
end
|
||||
|
||||
test "returns only posts with the given tag", %{blog: blog} do
|
||||
posts = blog.posts_by_tag("testing")
|
||||
|
||||
@ -59,6 +90,10 @@ defmodule Blogex.BlogTest do
|
||||
end
|
||||
|
||||
describe "all_tags/0" do
|
||||
test "excludes tags only on future-dated posts", %{blog: blog} do
|
||||
refute "future-only" in blog.all_tags()
|
||||
end
|
||||
|
||||
test "returns unique sorted tags from published posts", %{blog: blog} do
|
||||
tags = blog.all_tags()
|
||||
|
||||
|
||||
@ -8,12 +8,24 @@ defmodule Blogex.RegistryTest do
|
||||
def blog_id, do: :alpha
|
||||
def all_posts, do: [Blogex.Test.PostBuilder.build(id: "a1", date: ~D[2026-03-01], blog: :alpha)]
|
||||
def all_tags, do: ["elixir"]
|
||||
|
||||
def unfiltered_posts,
|
||||
do: [
|
||||
Blogex.Test.PostBuilder.build(id: "a1", date: ~D[2026-03-01], blog: :alpha),
|
||||
Blogex.Test.PostBuilder.build(id: "a-draft", date: ~D[2026-03-05], blog: :alpha, published: false)
|
||||
]
|
||||
end
|
||||
|
||||
defmodule BetaBlog do
|
||||
def blog_id, do: :beta
|
||||
def all_posts, do: [Blogex.Test.PostBuilder.build(id: "b1", date: ~D[2026-03-15], blog: :beta)]
|
||||
def all_tags, do: ["devops"]
|
||||
|
||||
def unfiltered_posts,
|
||||
do: [
|
||||
Blogex.Test.PostBuilder.build(id: "b1", date: ~D[2026-03-15], blog: :beta),
|
||||
Blogex.Test.PostBuilder.build(id: "b-future", date: ~D[2099-01-01], blog: :beta)
|
||||
]
|
||||
end
|
||||
|
||||
setup do
|
||||
@ -77,6 +89,21 @@ defmodule Blogex.RegistryTest do
|
||||
end
|
||||
end
|
||||
|
||||
describe "all_posts_unfiltered/0" do
|
||||
test "returns all posts including drafts and future-dated" do
|
||||
ids = Registry.all_posts_unfiltered() |> Enum.map(& &1.id)
|
||||
assert "a1" in ids
|
||||
assert "a-draft" in ids
|
||||
assert "b1" in ids
|
||||
assert "b-future" in ids
|
||||
end
|
||||
|
||||
test "sorts by date descending" do
|
||||
dates = Registry.all_posts_unfiltered() |> Enum.map(& &1.date)
|
||||
assert dates == Enum.sort(dates, {:desc, Date})
|
||||
end
|
||||
end
|
||||
|
||||
describe "blogs_map/0" do
|
||||
test "returns map keyed by blog_id" do
|
||||
map = Registry.blogs_map()
|
||||
|
||||
@ -51,9 +51,16 @@ defmodule Blogex.Test.FakeBlog do
|
||||
def description, do: get(:description)
|
||||
def base_path, do: get(:base_path)
|
||||
|
||||
def all_posts do
|
||||
def unfiltered_posts do
|
||||
get(:posts)
|
||||
|> Enum.filter(& &1.published)
|
||||
|> Enum.sort_by(& &1.date, {:desc, Date})
|
||||
end
|
||||
|
||||
def all_posts do
|
||||
today = Date.utc_today()
|
||||
|
||||
get(:posts)
|
||||
|> Enum.filter(&(&1.published and not Date.after?(&1.date, today)))
|
||||
|> Enum.sort_by(& &1.date, {:desc, Date})
|
||||
end
|
||||
|
||||
|
||||
@ -44,6 +44,12 @@ defmodule Blogex.Test.Setup do
|
||||
date: ~D[2026-03-12],
|
||||
tags: ["elixir"],
|
||||
published: false
|
||||
),
|
||||
build(
|
||||
id: "future-post",
|
||||
date: ~D[2099-01-01],
|
||||
tags: ["future-only"],
|
||||
published: true
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user