Checking if a page has a published attribute is of course
trivial:
> isPublished :: Page a -> Bool
> isPublished p =
> let published = getField "published" p in
> published /= "" && published /= "false"
Getting rid of non-published pages from the overviews (index.html
and posts.html) is then easy, we only have to filter them out
of the lists of pages produced by requireAllA with a custom
compiler:
> filterPublished :: Compiler (Page String, [Page String]) (Page String, [Page String])
> filterPublished = id *** arr (filter isPublished)
All that is then needed is to chain that compiler with addPostList,
for posts.html the line changes to
> >>> requireAllA "posts/*" (filterPublished >>> addPostList)
and for index.html:
> >>> requireAllA "posts/*" (filterPublished >>> (id *** arr (take 3 . reverse . sortByBaseName)) >>> addPostList)
This will suppress the links to the unpublished pages, but
will still generate the pages themselves. So we have to somehow
tag the pages we do not want to generate. "Either" sounds like
an appropriate choice:
> isPublishedPage :: Compiler (Page String) (Either (Page String) (Page String))
> isPublishedPage = arr (\p -> if isPublished p then Right p else Left p)
Of course, just tacking
> >>> isPublishedPage
at the end of the match "posts/*" $ do ... compiler leads to a type
error, as Either (Page String) (Page String) is not an instance of
Writable. But that is easily amended:
> instance Writable b => Writable (Either a b) where
> write p (Right b) = write p b
> write _ _ = return ()
Problem solved, except that I now get a runtime type error (!?)
when generating index.html and posts.html. That is solved by changing
filterPublished:
> import Data.Either (rights)
...
> filterPublished :: Compiler (Page String, [Either (Page String) (Page String)]) (Page String, [Page String])
> filterPublished = id *** arr rights
Phew.
Of course, this is still slightly inefficient, as it builds
the whole page before discarding it at the end. That can be overcome
by importing a few more methods from Control.Arrow. This will
not apply templates to unpublished pages:
> match "posts/*" $ do
> route $ setExtension ".html"
> compile $ pageCompiler
> >>> isPublishedPage
> >>> (id +++ (applyTemplateCompiler "templates/post.html"
> >>> applyTemplateCompiler "templates/default.html"
> >>> relativizeUrlsCompiler))
If you keep the original definition of filterPublished, you might instead
think about doing
> match "posts/*" $ do
> route $ setExtension ".html"
> compile $ pageCompiler
> >>> isPublishedPage
> >>> (applyTemplateCompiler "templates/embargo-post.html" ||| applyTemplateCompiler "templates/post.html")
> >>> applyTemplateCompiler "templates/default.html"
> >>> relativizeUrlsCompiler
so that the unpublished articles will be generated with another
compiler but not included in the lists. Or whatever you want.
Jay for compositionality.
- Florian.
Cheers,
Jasper
Yes, I think I could do that, just fork and send a pull
request?
I have some additional ideas, like timed releases, is there a
canonical way to get at the start time of the current build job,
or should I just use
unsafeCompiler (\_ -> getCurrentTime)
- Florian
Yes, pull requests are always welcome! :-)
Your code for getting the start of the current build job will work,
but I agree that it's a bit ugly, and it might not perfectly indicate
the start of the build job, due to laziness. I could add a canonical
way to get the time, if you have a use case which requires it.
Cheers,
Jasper