Get post attributes in Franklin

I’m in the process of migrating from Jekyll to Franklin for my static website (Garrek.org) hosted on GitHub. (In the near future, I will have LaTeX and plots and things, which Franklin seems to excel at). Jekyll has some nice features for retrieving site attributes and I’m trying to do something similar with Franklin. For example, my landing page is simply a list of all of my blog posts:

Jekyll snippet

{% for post in site.posts %}
<article>
	<h2>
		<a href="{{ post.url}}">{{ post.title }}</a>
	</h2>
	<p>{{ post.date | date_to_string }}</p>
	{{ post.content }}
</article>
{% endfor %}

I can almost replicate this by modifying hfun_recentblogposts() in utils.jl on the Julia website. But I can’t grab the post contents. I wrote a function that writes out the following:

Julia snippet within a function in utils.jl

write(io, """
              <article>
                <h2><a href="$url">$title</a>
                </h2>
                <p>
                  $sdate
                  <a class="permalink" title="Permalink" href="$url">
                  ⚓︎
                  ︎</a>
                </p>
                {{include $surl}}
              </article>
            """)

but I don’t know how to use include to get the post contents. ($surl gives the path of a post) I’m sure it’s not difficult, but I can’t find instructions in the Franklin.jl documentation.

  1. How do I get the contents of a blog post in my example?
  2. Is writing a Julia function like this in utils.jl the best way to go about implementing this sort of thing, or would it be better to put code in an html layout or something?

I understand that Franklin will work differently than Jekyll, and perhaps what I’m trying to do is not the best way to go about it; I’m struggling with how Franklin wants me to structure certain things.

My version of the "recent posts" function, which works except for grabbing the body of a post:
function hfun_recentposts()
    curyear = Dates.Year(Dates.today()).value
    ntofind = 2
    nfound  = 0
    recent  = Vector{Pair{String,Date}}(undef, ntofind)
    for year in curyear:-1:2020
        for month in 12:-1:1
            ms = "0"^(1-div(month, 10)) * "$month"
            base = joinpath("posts", "$year", "$ms")
            isdir(base) || continue
            posts = filter!(p -> endswith(p, ".md"), readdir(base))
            days  = zeros(Int, length(posts))
            surls = Vector{String}(undef, length(posts))
            for (i, post) in enumerate(posts)
                ps       = splitext(post)[1]
                surl     = "posts/$year/$ms/$ps"
                surls[i] = surl
                pubdate  = pagevar(surl, :published)
                days[i]  = isnothing(pubdate) ?
                                1 : day(Date(pubdate, dateformat"d u Y"))
            end
            # go over month post in antichronological orders
            sp = sortperm(days, rev=true)
            for (i, surl) in enumerate(surls[sp])
                recent[nfound + 1] = (surl => Date(year, month, days[sp[i]]))
                nfound += 1
                nfound == ntofind && break
            end
            nfound == ntofind && break
        end
        nfound == ntofind && break
    end
    #
    io = IOBuffer()
    for (surl, date) in recent
        url   = "/$surl/"
        title = pagevar(surl, :title)
		title === nothing && (title = "Untitled")
        sdate = "$(day(date)) $(monthname(date)) $(year(date))"
        write(io, """
              <article>
                <h2><a href="$url">$title</a>
                </h2>
                <p>
                  $sdate
                  <a class="permalink" title="Permalink" href="$url">
                  ⚓︎
                  ︎</a>
                </p>
                {{include $surl.html}}
              </article>
            """)
    end
    return String(take!(io))
end

Edit: clarified my question.

Hello! sorry I hadn’t seen your question, good that you also asked on Slack :slight_smile:

If you want the generated HTML corresponding to the full post you can use the fd_page_html local variable (I acknowledge that this is not documented, it’s a trick used for the RSS generation where there’s a possibility to show the full page content).

Yes generally when you want things a bit custom, it’s better to write a function for it so that you fully control the generated HTML (this is not unlike the Jekyll snippet you showed). You can play with the layout only if you want that generated HTML to be included in some specific HTML.

To give an example: each page will basically be head * content * foot (where head and foot are in your layout) but for some page you might want to use a different head or foot, you can do this by using {{ispage ...}} or {{isnotpage ...}} in the layout files. So in your case this is relevant if you would want the outer container of the list of blog posts to look something specific that’s different from the rest of your website.

I hope all this makes sense!

PS: side note, if you want custom “summary” of posts to show, then for this you should define your own local page variable a bit like the RSS description (in fact you can even re-use that variable), so you would do something like

+++
author = "Garrek"
short = "A short description of the post"
# or you could do (if you're also generating rss etc that could be handy)
rss_descr = "A short description of the post"
+++

# My post

the text of the awesome post

Thank you for the response! That makes sense, but I still can’t quite get there. I’m able to use the head * content * foot and {{ispage ...}}, etc. to use different layouts.

I tried a couple of things following your suggestions:

  1. I tried using {{for post in posts/*}} to iterate through each post, but I get the error The iterable 'posts/*' is not recognised. Using {{ispage posts/*}} elsewhere works as expected. (The file structure is posts/year/month/post.md). Also, my posts correctly display on a separate page where I just list the url of each post with a script in utils.jl.

  2. Inside a function in utils.jl, I created the variable content = pager(surl, :fd_page_html) and inserted that into write(io, """..."""), but nothing is written (other than the other variables I have defined, which are title = pagevar(surl, :title), the date, and a link). No error message either, the page just displays a list of titles with dates and permalinks.

  3. I was able to make a summary of posts as you describe. Defining short = pagevar(surl, :short) works correctly when I define short on each post. Curiously, rss_descr renders the text, “nothing”. I’m guessing this is because I have set up the RSS settings incorrectly somewhere.

I think in the first case, my syntax is incorrect. But I’m not sure what’s going on in the second case. Do you have any insight?

ah sorry I should have clarified, this is not allowed, the {{for...}} is very rudimentary and only unpacks iterables that are Franklin variables so for instance if you’ve defined x = [1, 2, 3] somewhere then {{for xi in x}}{{fill xi}}{{end}} will show 1 2 3 but you can’t loop over pages, here you should put the whole logic inside a hfun which I thought is what you were doing with the recent_posts function.

For 2-3 I think it’ll be easier if you point me to a repo where you have things and I’ll have a look, sorry for the inconvenience!

Got it. Yes, recent_posts is in a hfun, but I also have just a couple of things in the HTML files.

That’s very generous of you to look at the repo! It’s here (if needed, the Jekyll version is here). I call recent_posts in index.md and I modify HTML layouts in head.html. No rush, since it seems like you’re busy trying to get version 1.0 out the door.

What you have is fine, there was a (fairly sneaky) bug in Franklin when querying this specific page variable fd_page_html via pagevar. Long story short when it’s pagevar that is doing the query for fd_page_html, that variable wasn’t filled properly.

There’s a patch release (0.10.57) with the fix, should be in within the next 10 mins. Thanks for your patience and glad that led to a bug fix :slight_smile:

PS:

haha yes, there’s a 0.11 planned first which is a very significant refactoring of Franklin and I’ll need a fair bit of help stress testing that version before making it the effective 1.0, all this will take me a while though and 0.10.* is expected to be kind of like a “LTS” for some time so I’m always glad to fix bugs for that version! (who knows maybe we’ll hit 0.10.100 :joy:)

I see, that makes sense. I downloaded the patch and tried it out, but the page content is still not displayed. I fiddled around and couldn’t get it to work as expected. Any ideas?

Also, I merged my Franklin-generated repo into my main website repo and deleted the Jekyll code. It works correctly except for fd_page_html to grab the page HTML.

1 Like

I’ll check it out and try to see what’s going on :slight_smile:

@garrek isn’t that what you were expecting:

this is with the latest patch, maybe run serve(clear=true) to start from scratch and check that you get the same output?

Yes, that is what I was expecting. I tried running with serve(clear=true), but it still did not work. I did a test that worked:

I generating a completely new site with newsite(".") in a new directory (with the most recent patch). (I used serve() a few times at each step of the process to check). I created a directory called posts and made two dummy posts. I then copied just the recentposts function from my website into the new site’s utils.jl without modification. Then I deleted the content of index.md and replaced it with just {{recentposts}} and +++title = "blog"+++. After using serve() here, only the title and date of each post was displayed – no content. Then I used serve(clear=true) and it worked as expected. Subsequent calls also worked.

Perhaps there is something different about the template Franklin site from my site? But then why did it work on your end?

I’ll keep looking into it.

Ok. I got it to work, although I’m not sure what was wrong in the first place. I generated a new site from the default Franklin template and carefully replaced the template code with my code, checking with each change. Now it compiles correctly, but sometimes it doesn’t fill the post content. Usually this is when there’s a layout change. Running serve() again fixes it, though.

1 Like

Glad it’s working, re the sometimes; is this with the server running and after trying to manually refresh the page? There shouldn’t be anything different happening when a layout file is updated though changing layout elements triggers a “full pass” which takes a bit longer than just updating a single page, in some circumstances that can mean that the browser doesn’t get refreshed at the right time but if you hit refresh things will show fine.

If you can reproduce this with a sequence that leads to the content not showing, please ping me from an issue in your repo and I’ll have a look.

Good luck with your new website :slight_smile:

1 Like

Ok, I’ll take a closer look at this behavior and, if I can find something reproducible, ping you.

Thanks for all your help!

1 Like