• Home

  • Custom Ecommerce
  • Application Development
  • Database Consulting
  • Cloud Hosting
  • Systems Integration
  • Legacy Business Systems
  • Security & Compliance
  • GIS

  • Expertise

  • About Us
  • Our Team
  • Clients
  • Blog
  • Careers

  • CasePointer

  • VisionPort

  • Contact
  • Our Blog

    Ongoing observations by End Point Dev people

    Using GitHub for Blog Comments

    Phineas Jensen

    By Phineas Jensen
    November 14, 2017

    Last Saturday, November 11, we rolled out a new website that we’ve been working on for a few months. Part of this update was moving from Blogger as our blogging platform to static HTML generated by Middleman. We were more than happy to move away from Blogger for a variety of reasons, including its lack of HTTPS support for custom domains and how difficult it was to keep its templating and styling up to date with our main website. We were also able to move from blog.endpoint.com to www.endpoint.com/blog.

    The most obvious thing that is missing from Middleman’s blog extension is the lack of a commenting system. After exploring some options for comments, we settled on using GitHub issues and comments, inspired by Don Williamson’s post about doing the same thing. It’s a bit of an unconventional approach, so this post will explain how to use our commenting system and how we implemented it.

    Commenting requires a GitHub account, which is easy to sign up for and free, and the general target audience of our blog will often already have a GitHub account. At the bottom of each post will be a link to a GitHub issue at the top of the list of comments, if there are any. Click on the issue, write and post your comment, and it’ll appear on the blog post when you reload the page. It’s pretty simple.

    Now, for the gritty details. Don’s post shows the JavaScript he uses to find the correct GitHub issue and fetch its comments, but it required a bit of modification to work in our Middleman project. First, we needed a way to get the right GitHub issue number. Don’s example has that worked into the JavaScript with Hugo, fetching it from some parameter value, but I opted to create a hidden input on the page that would provide the right number:

    <input type="hidden" name="gh_issue" value="<%= current_page.data.gh_issue_number =>">
    

    Or in HAML:

    %input{type: "hidden", name: "gh_issue", value: current_page.data.gh_issue_number}
    

    And we can fetch that value with jQuery:

    var issue_id = $('[name="gh_issue"]').val();
    

    From there, we just had to modify the IDs referenced in Don’s JavaScript to match ours, and comments were working perfectly! Well, new comments were.

    Our blog is over 9 years old now, with over 1,300 posts and comments on many of those, and we needed to pull those existing comments over into GitHub as well. Actually copying the data wasn’t too difficult. I wrote a simple Python script to use the Blogger API to fetch posts and their comments, sort them by date, create an appropriately-named GitHub issue, and add the comments to them. Aside from GitHub’s anti-abuse detection system getting in the way a few times, it was an easy process.

    We wanted to preserve original author and timestamp information, so I had my script prepend the comment body with that information in a code block so it could be easily read by people reading comments on GitHub and parsed by JavaScript:

    original author: ...
    date: ...
    

    To parse that out, I wrote a quick and dirty regex that runs if I’m the user who created the comment:

    if (data.user.login == "phinjensen") {
      var regex = /^```\r?\noriginal author: ([^\r\n]+)\r?\ndate: ([^\r\n]+)\r?\n```/;
      var info = data.body.match(regex);
      if (info) {
        author_name = info[1];
        date = new Date(info[2]);
        data.body_html = data.body_html.replace(/^<pre><code>.+\r?\n.+\r?\n<\/code><\/pre>/, '');
      }
    }
    

    If that regex isn’t matched, then the author and date data is left as-is and parsed as normal. Here’s the full JavaScript we’re using:

    function formatDate(date) {
      var monthNames = [
        "January", "February", "March",
        "April", "May", "June", "July",
        "August", "September", "October",
        "November", "December"
      ];
    
      var day = date.getDate();
      var monthIndex = date.getMonth();
      var year = date.getFullYear();
    
      return monthNames[monthIndex] + ' ' + day + ', ' + year;
    }
    
    $(document).ready(function () {
    	var issue_id = $('[name="gh_issue"]').val();
    	var url = "https://github.com/EndPointCorp/end-point-blog/issues/" + issue_id
    	var api_url = "https://api.github.com/repos/EndPointCorp/end-point-blog/issues/" + issue_id + "/comments"
    
      $.ajax(api_url, {
        headers: { Accept: "application/vnd.github.v3.full+json" },
        dataType: "json",
        success: function(comments) {
          $(".comments").append("Visit the <b><a href='" + url + "'>GitHub Issue</a></b> to comment on this post.");
          $.each(comments, function(i, data) {
            var date, author_name;
    
            if (data.user.login == "phinjensen") {
              var regex = /^```\r?\noriginal author: ([^\r\n]+)\r?\ndate: ([^\r\n]+)\r?\n```/;
              var info = data.body.match(regex);
              if (info) {
                author_name = info[1];
                date = new Date(info[2]);
                data.body_html = data.body_html.replace(/^<pre><code>.+\r?\n.+\r?\n<\/code><\/pre>/, '');
              }
            }
            if (!date) {
              date = new Date(data.created_at);
            }
    
            var $comment = $("<div/>", {'class': 'comment'});
            if (!author_name) {
              $comment.append($("<img/>", {
                'src': data.user.avatar_url,
                'class': 'avatar-image'
              }));
            }
            var $body = $("<div/>", {'class': 'comment-body'});
            var $header = $("<span/>", {'class': 'comment-header'});
    
            if (author_name) {
              $header.append(author_name);
            } else {
              $header.append($("<a/>", {'href': data.user.html_url, 'text': data.user.login}));
            }
            $header.append(" commented on " + formatDate(date));
            $body.append($header);
            $body.append($("<div>" + data.body_html + "</div>", {'class': 'comment-body',}));
            $comment.append($body);
            $(".comments").append($comment);
          });
        },
        error: function() {
          $(".comments").append("Comments are not open for this post yet.");
        }
      });
    });
    

    Huge thanks to Don Williamson for sharing his implementation of this idea! If you have any feedback for us and how we’re doing it, let us know in the comments!

    javascript community api static-site-generator


    Comments