Skip to content

Instantly share code, notes, and snippets.

@jdvp
Last active September 26, 2021 07:53
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jdvp/3444314520259a827b90d392803e94ae to your computer and use it in GitHub Desktop.
Save jdvp/3444314520259a827b90d392803e94ae to your computer and use it in GitHub Desktop.
OUTDATED : See the file here instead: https://github.com/jdvp/jekyll-comments-google-forms Code required for adding a Google Forms-based commenting system to Jekyll blogs. Necessary for the https://jdvp.me/articles/Google-Forms-Jekyll-Comments article.
{% if site.comment-read %}
<!-- Load Required 3rd-Party Scripts. We use the following:
* jquery for DOM manipulation
* jquery-csv for parsing Google Sheets CSV data to JSON
* validator for escaping user-entered comments to prevent malicious code input in comments
Make sure to update these to the latest versions every once in a while.
-->
<script
src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
crossorigin="anonymous"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/1.0.11/jquery.csv.min.js"
integrity="sha512-Y8iWYJDo6HiTo5xtml1g4QqHtl/PO1w+dmUpQfQSOTqKNsMhExfyPN2ncNAe9JuJUSKzwK/b6oaNPop4MXzkwg=="
crossorigin="anonymous"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/validator/13.5.2/validator.min.js"
integrity="sha512-vZEoK8xRncku4g5GANHh5zobUjeCMbZkSEahrADeAcRlk/1Ignf8sAKojkV4RZLkPw145+ILJFisI2pnjsPtGQ=="
crossorigin="anonymous"></script>
<!-- Our own JS starts here -->
<script>
var thisPageUrl = "{{ include.url }}";
var visibleComments = [];
/* Format the comment's date to our desired format */
function formatDate(stringDate) {
var date = new Date(stringDate);
var dateOptions = { year: 'numeric', month: 'long', day: 'numeric'};
var timeOptions = { hour: 'numeric', minute: 'numeric' };
return `${date.toLocaleDateString("en-us", dateOptions)} at ${date.toLocaleTimeString("en-us", timeOptions)}`;
};
/* Called when the comments have been loaded or relaoded.
Comments that we have already displayed on the page will be ignored */
function displayComments(comments) {
/* if we have no comments, show the 'no comments available' section */
if (comments == null || comments === "") {
if (visibleComments.length == 0) {
$("#no-comments").show();
}
return;
}
/* Add headers so json objects are named properly */
comments = `timestamp,name,comment,isAuthor\n${comments}`;
commentList = $.csv.toObjects(comments);
/* iterate over each comment that came from Google Sheets */
commentList.forEach(function(element) {
/* don't re-add existing comments */
if (visibleComments.includes(JSON.stringify(element))) {
return;
}
/* If the author was the one who commented back, add this 'verified' logic */
var verifiedAuthorTag = "";
if (element.isAuthor === "TRUE") {
verifiedAuthorTag = "&nbsp;<strong>(Author)</strong>";
}
/* Create the new item using HTML and append it to the user-visible comment section */
var newItem = $(`
<div class="comment-item">
<p class="commenter-name">${validator.escape(element.name)}${verifiedAuthorTag}<small>${formatDate(element.timestamp)}</small></p>
<div>
<p>${validator.escape(element.comment).replace(new RegExp('\r?\n','g'), '<br />')}</p>
</div>
</div>
`).hide();
$("#commentSection").append(newItem);
newItem.fadeIn();
visibleComments.push(JSON.stringify(element));
/* Hide the 'no comments available' section since we have added a comment */
$("#no-comments").hide();
});
};
/* Calls to the Google Sheets gviz API to load the comment data
Since we are storing comments for all of our blog articles in one sheet, we filter
by page URL. */
function reloadComments() {
/*
Use SQL in the gviz URL to load the correct csv.
In this implementation, columns are mapped to the following:
A - timestamp, the time the comment was left
B - page url, the page the comment was left on
C - name, the text left in the comment name field
D - comment, the text left in the comment field
E - isAuthor, boolean indicating if the comment was left by the actual author
*/
var sqlStatement = encodeURIComponent(`SELECT A, C, D, E WHERE B = '${thisPageUrl}'`);
fetch(`{{ site.comment-read }}/gviz/tq?tqx=out:csv&sheet=comments&tq=${sqlStatement}&headers=0`)
.then(response => response.text())
.then(response => displayComments(response));
};
/* Reload the comments when the page first loads */
reloadComments();
/* encodes the form data for http transport */
const encodeFormData = (data) => {
return Object.keys(data)
.map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
.join('&');
};
/* Posts a comment */
function postComment() {
var username = $("#comment-name").val();
var comment = $("#comment-comment").val();
fetch(`{{ site.comment-post }}/formResponse`, {
method: 'POST',
mode: 'no-cors',
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: encodeFormData({
"{{ site.comment-post-fields[0] }}" : thisPageUrl,
"{{ site.comment-post-fields[1] }}" : username,
"{{ site.comment-post-fields[2] }}" : comment
})
}).then(response => {
$("#comment-name").val('');
$("#comment-comment").val('');
reloadComments();
})
.catch(error => {console.log(error);});
return false;
};
</script>
<!--
HTML for displaying the actual comment section.
It has a state for oth when the article has comments and when it does not.
-->
<h4 class="post-subtitle">Comments</h4>
<div id="commentSection" class="comments">
<div id="no-comments" class="post-info">
<p>There are currently no comments on this article, be the first to add one below</p>
</div>
</div>
<h4 class="post-subtitle">Add a Comment</h4>
<div class="comments">
<div class="post-info">
<p>Note that I may remove comments for any reason, so try to be civil. If you are looking for a response to your comment, either leave your email address or check back on this page periodically.</p>
</div>
<div class="comments-form">
<form onsubmit="return postComment()" id="comment-form" autocomplete="off">
<ul>
<li><input id="comment-name" name="username" type="text" placeholder="Name" required="" value=""></li>
<li><textarea id="comment-comment" name="comment" placeholder="Comment" required=""></textarea></li>
<li><input id="comment-submit" type="submit" value="Post"></li>
</ul>
</form>
</div>
</div>
<!--
Styling information.
This might typically be in an scss file but is added here so only a single
file needs to be added to a project trying to implement this feature.
-->
<style>
@import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro");
.comment-item {
position: relative;
padding: 0.5em 2.5em;
background: white;
font-family: "Source Sans Pro", sans-serif;
width: 95%;
margin: 1em auto;
box-shadow: 0px 0px 2px 2px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
.comment-item:after {
content: "";
position: relative;
display: block;
clear: both;
}
.comment-item p {
line-height: 1.6;
}
.comment-item small {
padding-left: 0.7em;
color: #999;
font-size: 0.8em;
}
.commenter-name {
color: #367588;
text-decoration: none;
font-size: 1em;
font-weight: 700;
}
.commenter-name strong {
color: #e91e63;
text-decoration: none;
font-size: 1em;
font-weight: 700;
}
.comments {
border-top: 0.5px solid #e5e5e5;
padding-top: 1rem;
position: relative;
}
.comments-form {
margin-left: 1em;
margin-right: 1em;
width: 95%;
font-family: "Source Sans Pro", sans-serif;
}
.comments-form ul {
list-style: none;
margin-left: -40px;
}
.comments-form ul li {
margin-bottom: 15px;
}
.comments-form ul li input[type='submit'] {
border: 0;
border-radius: 8px;
padding: 10px 10px;
background: #9ccc65;
color: #fff;
font-family: "Source Sans Pro", sans-serif;
font-weight: bold;
font-size: 0.9em;
text-transform: uppercase;
margin-bottom: 20px;
width: 25%;
outline-style: none;
}
.comments-form ul li input[type='text'], .comments-form ul li textarea {
width: 50%;
padding: 10px;
border: 0;
border-radius: 8px;
background: #f7f7f7;
font-family: "Source Sans Pro", sans-serif;
}
.comments-form ul li input[type='text']::-webkit-input-placeholder, .comments-form ul li textarea::-webkit-input-placeholder {
font-family: "Source Sans Pro", sans-serif;
}
.comments-form ul li input[type='text']::-moz-placeholder, .comments-form ul li textarea::-moz-placeholder {
font-family: "Source Sans Pro", sans-serif;
}
.comments-form ul li input[type='text']:-ms-input-placeholder, .comments-form ul li textarea:-ms-input-placeholder {
font-family: "Source Sans Pro", sans-serif;
}
.comments-form ul li input[type='text']::-ms-input-placeholder, .comments-form ul li textarea::-ms-input-placeholder {
font-family: "Source Sans Pro", sans-serif;
}
.comments-form ul li input[type='text']::placeholder, .comments-form ul li textarea::placeholder {
font-family: "Source Sans Pro", sans-serif;
}
.comments-form ul li input[type='text']:focus, .comments-form ul li textarea:focus {
background: #fff;
font-family: "Source Sans Pro", sans-serif;
}
.comments-form ul li textarea {
width: 100%;
height: 80px;
resize: vertical;
min-height: 80px;
}
</style>
{% endif %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment