Problem Solving Threaded Comments in Shopify Blogs

Problem Solving Threaded Comments in Shopify
I recently had a potential client ask for threaded comments (where replies are shown below the original comment) in a Shopify blog, which is not a feature the platform offers natively. It got me thinking about how we might be able to make it work.

I thought it’d be interesting to share the thought process I went through when approaching this problem, since one of the key components of a web developer’s job is tackling implementation problems like this but the actual problem solving is not something we talk about as much as we probably should.

The Problem

Shopify blog comments are a single level, with no reply hierarchy. WordPress does offer threaded comments, and it’s what many blogs use so that’s become a familiar feature. Beyond familiarity, threaded comments are excellent for building community and generating a real conversation with readers.

How can we get comment replies in Shopify to be visually linked to the original comment so as to show the conversation?

The Thought Process

The first set of questions that came up when I started thinking about this problem:

  • Is there a way to have a reply link on the comments like you’d see on WordPress so anyone can reply to any top level comment (we’re only aiming for one level of replies for now)?
  • If not, is there a way to have the site owner able to reply to comments, even if it isn’t something the general public is able to do?

A Potential HTML & jQuery End Model

My initial thought was that we could associate replies to the original comment in the thread using the comment id, which is something Shopify stores for each comment (accessed via the liquid tag {{ comment.id }}).

My thought was that if we put the parent comment ID in the markup for both the parent comment and any reply comments, we could then use jQuery to move the replies up just under the parent (using append() or something similar).

This would look something like this:

<div id="comment-1">
    I'm the parent comment.
</div>
<div id="comment-2">
    I'm an unrelated comment.
</div>
<div id="comment-3" class="comment-1">
    I'm a response to the first comment.
</div>
<div id="comment-4" class="comment-1">
    I'm another reply to the first comment.
</div>

With that structure, we could use jQuery to find all the comments with the class comment-1 and then move them after the comment with the matching ID.

Getting the Comment ID in the Markup

The problem now is that we’ve got to associate a different comment’s ID with the replies somehow. Some ways this could potentially work (depending on Shopify’s system constraints):

  1. We include a hidden field in the comment form that contains the parent ID (probably put there by jQuery when the reply link is clicked?) and that pushes that information to the comment object or to a metafield. Then we pull that out from the comment object in the class.
  2. Only the shop owner can submit replies, using the backend to write them and including the parent comment ID as a metafield on their comments. We use that metafield for the class.
  3. Only the shop owner can submit replies, including the parent comment ID in the body of their comment in a way that we can strip out and use in jQuery but that doesn’t look terrible if the jQuery fails for some reason (javascript off, future bug, etc.).
  4. The comment ID gets submitted in the body of the comment, put there somehow when the reply button is clicked, and in a way that we can strip it out and use it but it doesn’t look terrible if jQuery fails (maybe invisible or on submit).

That list makes it seems like those ideas all came up together, but really it was a little more linear. The first one was my ideal case, but was quickly shot down because the comment form can’t be modified to submit information that way (womp womp).

That led me to think about the second idea, since for a lot of content types in Shopify we can add custom metafields in the admin. Unfortunately, it seems as though metafields are not a thing for comments (womp womp again).

Those two dead ends lead me to the final two choices, where we’re submitting the values via the field we do have (which is pretty much just the comment content area). The fourth is likely preferable as it’s the most flexible and the most automated (less chance of things breaking), so that’s what we’ll be tackling.

Further Considerations

Did you read this far hoping for a solution? Sorry, I don’t have one for you yet. As of this writing we haven’t tackled the problem any further, but if/ when we do I’ll be sure to write it up.

For now here the things on my mind for the next phase of tackling this problem:

  • How do I put information from my parent content markup (the ID) into the comment textarea on a link click? (Here’s a Stack Overflow thread that may help)
  • What does that information in the textarea look like, if it’s even visible? How can I make sure it gets submitted without getting accidentally edited first?
  • How does that information get parsed in the printed comment so that it isn’t in the comment content and is in the markup.

Tips for Project/ Flat Rate Invoicing

Tips for Project or Flat Rate Invoicing
I recently had a question come my way from a new freelancer about pricing client work. She’d already created a draft invoice for the work and was looking for feedback on whether she was pricing appropriately and thinking comprehensively about the work.

While I didn’t provide her with feedback on her specific pricing, I did send some quick thoughts on the way she was itemizing the tasks and describing the scope of the project and thought they’d be worth sharing here for others who might have similar questions.

She had her invoice itemized out something like this:

Service Time Cost
Domain & Hosting Setup; WordPress Install* A hrs at $X/hour $YYY
Theme Research & Installation, Minor Customization B hrs at $X/hour $YYY
Content Entry (5+ Pages, Multiple Image Galleries) C hrs at $X/hour $YYY
*Cost of domain registration, hosting, and WordPress theme not included

The Good Stuff

There are a lot of good things about the way this is set up already!

Itemized Services

I was happy to see this person listing out various components of the service they’re providing the client, breaking things out into groups of similar tasks. This really helps clients understand what they’re paying for and also makes sure everyone is on the same page regarding project scope.

Research Time Included

It’s also great that she included research into themes as part of the project scope, since in this particular project she was helping the client find a suitable pre-made WordPress theme to use. It takes time to match up client requirements to available themes and that time should be accounted for and clearly delineated in the invoice or estimate.

License and Recurring Costs Not Included

I was also very happy to see that she had specifically noted that recurring costs (hosting, domain registration) and the cost of the theme are not included in the quoted total. I feel pretty strongly about not including those types of costs in base invoices and instead having the client have ownership over their accounts and pay the actual cost of whatever licenses are needed so that they have those things in their name.

I also usually note in my invoices that web font license costs aren’t included, as that’s another semi-hidden cost that the client should be aware of from the outset and that I won’t cover myself.

Areas for Improvement

While the info this freelancer included was a great start, there are also some definite areas for improvement and further specificity.

In fact, my general rule with estimates and invoices is to be as specific as possible and also to note that things that aren’t listed aren’t included. This helps protect me against scope creep and also makes sure the client knows they should ask early on in the process if they’re looking for something specific that isn’t listed on the estimate.

In this example, I had three suggestions for things to improve on.

Avoid Relative Terms

The phrase “minor customization” raised a big flag for me, as it’s unclear what “minor” would include. A lot of the time, things a client may think are “minor” actually aren’t as straightforward as they may seem, so it’s best not to use relative or subjective terms and instead be really clear about what you’re going to customize and what the limits are. I might specify “customization of colors, fonts, and header graphics” or something like that.

Include Hard Limits

I was also concerned by content uploading including “10+ pages” and “multiple” image galleries. This is another place where you want to be really specific and provide caps rather than minimums or general terms (“up to X pages” and “up to X galleries with up to X images each). Otherwise you are going to end up hating life when you’re uploading image 125 of 160 on your 15th gallery page.

Overage Plan

While I don’t actually include this in my estimates and invoices (it’s in my contract instead), I also prompted this freelancer to consider what she would do if she went over the hours she listed or if the client needed more than outlined in the project scope.

I don’t include hour indications in my estimates unless it’s a line item specifically for hourly work, as the hours are irrelevant to the client when I’m really quoting on a project or output basis. If I go over my predicted hours getting to the agreed-upon scope, that’s on me and it doesn’t change the quote for the client.

On the flip side, if the client ends up wanting more than what we agreed upon, it’s good to be clear about how you handle that. For me, I say that I’m ok with adding to scope but the client has to know that that increases the cost of the project and may also impact the time frame.


Overall, this freelancer was starting from a good place with her invoices and just needed a few small changes to make things more clear and concrete for both parties, which ensures a much smoother process overall.

Automating Theme Development Presentation at WordCamp Philly 2014

Over the weekend I got to speak at WordCamp Philly in the Developer Track, presenting on automating parts of the theme development workflow.

View on Speaker Deck

It was my first time speaking at a conference, much less giving a technical presentation (I’ve taught classes on technical topics but that’s pretty different than a conference presentation in front of a peer group). I was way more nervous than I expected (especially with some people I really admire in the WordPress world in the room), but I’m pretty happy with how it turned out!

I started out by debunking three reasons developers might have for not checking out some of the newer more automated development tools:

Herd behavior/ mind over machine

Too much too learn/ magic

I am an island/ No man is an island

Then I moved through a rundown of various things that can be automated and how you’d automate them broken up by rough phases of theme development.

theme development workflow phases

I covered a lot of tools in my 35 minutes, including Sass, Gulp, Autoprefixer, Yeoman, Git, Deploy and more.

Gulp styles task

Overview of a Gulp task to process Sass, prefix the CSS output, and minify

Demo of a Yeoman generator process for my Emi Starter Theme

Demo of a Yeoman generator process for my Emi Starter Theme

Initializing Git for a theme

Working with Git as part of a deploy process

There were a ton of other amazing sessions at the conference as well, and I believe they’ll all be up on WordPress.tv shortly if they aren’t already, so even if you weren’t around (or if you were but you missed a session) you can see them.

You can also check out my full slide deck if you’d like to check out the rest of the info that way.

Automating is about replacing grunt work with magic.

Thanks so much to WordCamp for having me!

How to Use Shopify Blogs for Gallery or Grid Layouts

It can be tricky to create complex informational page layouts in Shopify since there aren’t custom content types and custom fields (via metafields) are pretty limited. The two most common workarounds I use are building page layouts out of the content of multiple different Shopify pages, and using Shopify’s blogging engine to create galleries or grid layouts.

This tutorial will cover the latter, which is a technique I certainly didn’t invent but have refined for my own purposes and haven’t seen written up much.

Overview & Limitations

This method makes sense for layouts where there’s repeated content and each block needs specialized formatting.

Examples

A common example is a grid-based gallery where each individual item clicks through to either a link or a larger image in a lightbox, as in Rare Device’s Past Shows page:

Rare Device Past Shows page

I’ve also used it for things like tabbed areas on informational pages, as in the SCOUT Lifestyle Retailers page:

SCOUT Lifestyle Retailers page

The snippets below are actually from a press page implementation that isn’t live yet, but has a grid of magazine covers that either link externally or open a lightbox image when clicked.

Why It Works

There are three primary reasons that blogs are a viable and useful solution:

1. You can have as many distinct “blogs” as you want within Shopify. Using a blog for some other content type doesn’t take away from the ability to maintain a regular blog, so it’s a great choice for organizing other complex, entry-based types of content.

2. Blog entries have a useful number of fields built in. Unlike pages, which really just have a title and a content WYSIWYG area, blog posts have multiple fields: Title, Content, Excerpt, Tags, and Date. Additionally, I suppose one could use the meta/ SEO fields if so inclined.

3. Blogs are easy to loop through. Loops are an ideal way to generate repeated blocks of content, and blogs naturally lend themselves to this since they’re meant to be displayed in this way.

Limitations

On the other hand, there are a couple of limitations:

1. There’s an administration burden. Moving between blog posts to manage page content can feel inefficient and like a hack to the site administrator. Also, the client will need to remember a pattern of content entry in order for this model to work. This can be mitigated with client education but is something to consider, especially for clients who are more likely to struggle with this kind of thing.

2. Thumbnail sizes are tricky. As with any Shopify image situation, creating consistent thumbnail sizes can be challenging. Thumbnails generally need to be sized prior to being uploaded for the best results.

Content Setup

Generally you’ll want to start your setup by creating a new Shopify blog. You can do this in the Shopify admin by going to the Blog Posts section and clicking on “Manage Blogs” at the top right. You’ll want to give your blog a short, simple name and make sure you’ve noted the blog handle for use in your code:

Shopify blog handle

Then, you’ll create a post for each content block. For the press implementation, I typically put the publication name in the title field, the large pop up image or the URL in the main content area (just pasted right into the text area, not inserted as using the link button), and the thumbnail in the excerpt area. I usually use the dates on the posts to order them, just like you’d have a regular blog displayed in reverse chronological order.

Make sure your posts are assigned to the new blog!

Create the Page Template

Once there are a couple of posts to work with, the next step is to create the custom page template. In the Shopify admin, you would do this by navigating to the Themes section and using the Template Editor to create a new page template, for example page.press.liquid. You may want to base this on an existing template so that you have the appropriate markup for your theme’s layout.

You’ll want to include a snippet something like this wherever your content blocks should appear:

{% for article in blogs.press.articles %}
    <div class="press-item">
        {% if article.content contains "<img" %}
            {% assign src = article.content | split: 'src="' %}
            {% assign src = src[1] | split: '"' | first | replace: '//cdn', 'http://cdn'; | replace: 'http:http://';, 'http://'; | remove: 'https:' %}
            <a href="{{ src }}" class="fancybox">
                {{ article.excerpt }}
                <div class="info">
                    <span class="title">{{ article.title }}</span>
                    <span>{{ article.published_at | date: "%B %Y" }}</span>
                </div>
            </a>
        {% else %}
            <a href="{{ article.content }}" target="_blank">
                {{ article.excerpt }}
                <div class="info">
                    <span class="title">{{ article.title }}</span>
                    <span>{{ article.published_at | date: "%B %Y" }}</span>
                </div>
            </a>
        {% endif %}
    </div>
{% endfor %}

This loops through the articles in the blog you created (make sure to replace the “press” handle with your blog’s handle). For each article, it creates a <div> with class press-item.

Then, there’s a conditional that checks whether the article content contains an image. The snippet within the first segment of the conditional was pulled from this Shopify forum discussion and extracts the image URL from the content area so that we can display it in the pop up. The pieces that do that extraction are:

{% assign src = article.content | split: 'src="' %}
{% assign src = src[1] | split: '"' | first | replace: '//cdn', 'http://cdn'; | replace: 'http:http://';, 'http://'; | remove: 'https:' %}

And then the resultant URL is used in the link: <a href="{{ src }}" class="fancybox">.

If there is not an image, the second part of the conditional instead pulls the URL from the content into the link instead.

In both cases, the content inside the link includes the excerpt field contents (the thumbnail), the article title, and the date.

After creating the page template, you can create the page itself and assign the template.

In this particular example, the remaining work would be in styling the page and integrating Fancybox or another lightbox system. Many Shopify themes already have a lightbox feature, so it’s just a matter of tapping into it on this template page.

Redirect Blog Pages

Finally, a nice finishing touch is to ensure that site visitors won’t be able to accidentally land on the blog archive page or the single article pages for this content. Instead, we’ll use some simple javascript to redirect them to the page we just created:

{% if template == 'blog' and blog.title == 'Press' %}
   <script>
      window.document.location.href = "/pages/press";
   </script>
{% elsif template == 'article' and blog.title == 'Press' %}
   <script>
      window.document.location.href = "/pages/press";
   </script>
{% endif %}

This snippet checks both for the template that’s being loaded and for the blog title, in case there are multiple blogs on the site.

Now you’ve got a page template pulling in the specified content from a blog, and a system in place to ensure the blog cannot be accessed directly!

Display Top WordPress Posts by Page Views

A recent client project has a component that shows top posts by views, which took some figuring out. WordPress doesn’t log views by post out of the box, but the Jetpack plugin does so that’s what we used as our data set. There are a couple Stack Overflow threads that talk about querying the posts by view count using Jetpack but none of them have the full code necessary so I thought it’d be useful to share how I did it.

Posts by views carousel

the end result

The post display is done using a custom loop via WP_Query, and the carousel in this particular example is done with bxSlider, which I use all the time for sliders and carousels.

Full Template Code

Here’s the full code for that area, which would go in the template file for the particular page or area you’re displaying the posts within. In this case, they’re on every page in the header so they’re in the theme’s header.php file.

The first and last lines are a conditional that checks for the stats function. If it returns false because the function isn’t there (e.g. if there’s no Jetpack installed) then nothing else will happen, preventing errors or other weirdness.

The Breakdown

Let’s run through the pieces of that snippet. (I won’t be covering the CSS and jQuery in this tutorial, since the focus is on getting the content to appear on the page, but let me know in the comments if you’d like to see a tutorial on carousels.)

Grab Top Posts’ IDs

The first few lines are getting the posts by views as registered by Jetpack:

The first line pulls up the top posts by views, using the last 21 days’ worth of data (that’s the first parameter in the array). The -1 value means that we are not limiting the number of posts returned. In the client project, I used custom fields for the number of days so that the client can change that if they’d like, and I also set a high but not unlimited value for the items returned since unlimited seems kind of unnecessary (the default is 100 if you omit that parameter entirely).

Set Custom Query Arguments

The remaining lines in that snippet create an array of post IDs for the posts returned by that first line, which is key because we need that array to plug into the post__in parameter for our query:

If you’ve ever built a custom loop using WP_Query, the above snippet will likely be familiar. It’s the arguments for our query, the first of which, post_type specifies that we want to pull only posts (the stats will return both posts and pages).

Major hat tip to Greg Rickaby for helping me level this up by adding a transient, which is essentially like a cookie and stores the query for a specified length of time, which speeds things up.

The other two parameters are post/ page specifics. First, post__in (that’s two underscores in there) specifies that we only want to return posts with IDs in that array we just built. Finally, posts_per_page limits the number of posts displayed on the page, overriding any other pagination you may have going on (from your regular loop settings, for example). I gave the client a setting for that value as well, so that she can control the number of posts in the carousel herself.

Create The Loop

Finally, the rest of the code is a standard WordPress loop structure:

In this construct, each item is wrapped in an <a> element (could just as well be a <div>) that links to the post permalink, and then the post thumbnail and title are displayed. I’ve arranged the markup so that I can have the title appear over the thumbnail on hover using CSS.

And there you have it, a snippet that returns top posts by views!