Search code examples
twitterhugohugo-shortcode

Hugo Shortcode that uses JSON throws "bad character U+0022 error"


I’m trying to make a shortcode with two parameters, a CSS class and a Tweet ID. My starting point is this example https://gohugo.io/content-management/shortcodes/#tweet that shows how the default Hugo Twitter shortcode is used

{{< tweet 877500564405444608 >}}

But I want to use it like this in post.md:

{{< tweet-single class="alignright" id="877500564405444608" >}}

to produce the html:

<div class="tweet-in-post alignright">
<twitter-widget class=....
</div>

but using this in tweet-single.html

<!-- tweet-single -->

<div class="tweet-in-post {{ .Get "class" }}">

{{ (getJSON "https://api.twitter.com/1.1/statuses/oembed.json?dnt=1&hide_thread=1&id={{ .Get "id" }}") }}

</div>

gives me the error bad character U+0022 ‘"’.

These docs https://gohugo.io/templates/data-templates/#call-the-functions-with-a-url show how to call JSON function in Hugo, but I don't know how to use those examples to get my shortcode to work.


Solution

  • The best way to customize a built-in shortcode is to start from its code.

    The link you provide shows how to use the tweet shortcode, but it doesn't show what its code actually looks like. It takes some digging into the Hugo source code to actually find what the built-in shortcodes look like.

    You can find them here: https://github.com/gohugoio/hugo/tree/master/tpl/tplimpl/embedded/templates/shortcodes.

    As of the current commit (67f9204), this is what the twitter shortcode looks like:

    {{- $pc := .Page.Site.Config.Privacy.Twitter -}}
    {{- if not $pc.Disable -}}
    {{- if $pc.Simple -}}
    {{ template "_internal/shortcodes/twitter_simple.html" . }}
    {{- else -}}
    {{- $url := printf "https://api.twitter.com/1/statuses/oembed.json?id=%v&dnt=%t" (index .Params 0) $pc.EnableDNT -}}
    {{- $json := getJSON $url -}}
    {{ $json.html | safeHTML }}
    {{- end -}}
    {{- end -}}
    

    So that is your starting point.

    But this example is particularly tricky because this shortcode uses a style set up by the twitter api and it is particularly hard to fiddle with it.

    This post can help you there.

    Another difficulty here is that, if you look at the twitter shortcode, you can see that the tweet ID is not called by {{ .Get "ID" }} (a named params), but by (index .Params 0) (a positional params). And Hugo does not let you mix named and positional params.

    So you cannot use something like {{ .Get "class" }} as you tried. Instead, you need to use a positional params such as {{ .Get xx }}, xx being the position of your params within the shortcode call.

    Here is a possible solution:

    {{- $pc := .Page.Site.Config.Privacy.Twitter -}}
    {{- if not $pc.Disable -}}
    {{- if $pc.Simple -}}
    {{ template "_internal/shortcodes/twitter_simple.html" . }}
    {{- else -}}
    <blockquote class="twitter-tweet tw-align-{{ .Get 0 }}" data-lang="en">
      {{- $url := printf "https://api.twitter.com/1/statuses/oembed.json?id=%v&dnt=%t" (index .Params 1) $pc.EnableDNT -}}
      {{- $json := getJSON $url -}}
      {{ $json.html | safeHTML }}
    </blockquote>
    {{- end -}}
    {{- end -}}
    

    As you can see, I added some <blockquote> customization, following the answer of the post I mentioned above and I am using {{ .Get 0 }} for the position. This means that I now have to adjust the params position for the tweet ID to 1.

    If you name this shortcode tweet-single.html, you can then call it with:

    {{< twitter-single right 877500564405444608>}}
    

    to have the tweet to the right.

    To have the tweet centered, it would be:

    {{< twitter-single center 877500564405444608>}}
    

    Note that right or center do not take quotes since they are positional params.


    Alternative approach:

    If this approach does not work for you for some reason, a totally different approach would be to re-create the entire formatting of the tweet (without using what the twitter api does).

    This is what Hugo does if you use twitter_simple.html instead of twitter.html.

    If you look at twitter_simple.html, you can see that they are omitting the styling script and are instead recreating some simpler formatting for the .twitter-tweet class:

    {{- $pc := .Page.Site.Config.Privacy.Twitter -}}
    {{- $sc := .Page.Site.Config.Services.Twitter -}}
    {{- if not $pc.Disable -}}
    {{- $id := .Get 0 -}}
    {{- $json := getJSON "https://api.twitter.com/1/statuses/oembed.json?id=" $id "&omit_script=true" -}}
    {{- if not $sc.DisableInlineCSS -}}
    {{ template "__h_simple_twitter_css" $ }}
    {{- end -}}
    {{ $json.html | safeHTML }}
    {{- end -}}
    
    {{ define "__h_simple_twitter_css" }}
    {{ if not (.Page.Scratch.Get "__h_simple_twitter_css") }}
    {{/* Only include once */}}
    {{  .Page.Scratch.Set "__h_simple_twitter_css" true }}
    <style type="text/css">
      .twitter-tweet {
      font: 14px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
      border-left: 4px solid #2b7bb9;
      padding-left: 1.5em;
      color: #555;
    }
      .twitter-tweet a {
      color: #2b7bb9;
      text-decoration: none;
    }
      blockquote.twitter-tweet a:hover,
      blockquote.twitter-tweet a:focus {
      text-decoration: underline;
    }
    </style>
    {{ end }}
    {{ end }}
    

    You can play with this code until you have a customization that suits you.