Add Excerpts to a SharePoint 2013 Blog

By

Blogging and the Internet seem to go hand-in-hand, so it's only fitting that Microsoft should include a ready-made blogging app as part of SharePoint. It includes most of the things you would expect of a blogging app, but there are some areas where it falls short. How a SharePoint blog displays lists of articles is one of them. It seems to take an all-or-nothing approach of either displaying only the title, date, and byline, as with the categories and archives views, or displaying the entire article text, as with the blog home page. Out of the box, there's no middle ground.

Today, I want to show you one solution for displaying excerpts (instead of the entire article) in a SharePoint blog. Before I dive in, I want to preface this with a couple of disclaimers. Firstly, I assume that you are comfortable enough with SharePoint 2013 to know how to edit pages and add columns to SharePoint lists. Also, as with programming in general, there are lots of ways to solve this problem. When evaluating this solution for your own use, you should consider your project's needs, your comfort level with the method described, and your personal preferences.

Let's dive in...

Step 1: Add an Excerpt column

Before your blog can have excerpts, it needs somewhere to put them. While you could just truncate the contents of the Body column, storing the excerpt separately means that you can give your content authors more control over what's displayed with minimal effort. Here's how to set that up:

  1. Navigate to your blog subsite, then go into Site Settings.
  2. Within Site Administration, select the Site libraries and lists, then choose Customize Posts.
  3. Create a column named "Excerpt" with the type Multiple lines of text. You can leave the other options at their defaults or modify them as you see fit, however this article assumes that the Excerpt column allows enhanced rich text.

Now, when you create a new blog post, you should notice an additional Excerpt field at the bottom of the form. That's great, but it also means yet another box your content authors have to fill in. Who wants to type in the same information twice? And what happens if they forget?

Step 2: Populate the Excerpt field automatically

Let's save your authors some time by copying the first paragraph of the Body field over to the Excerpt field automatically. Even better, let's make it so that changes in the Body field get copied over to the Excerpt field as well, unless the author has modified the Excerpt field separately, as we don't want to undo their changes. Here's how:

<script>
// Automatically copy the first paragraph from the Body field to the Excerpt field anytime the Body field loses focus, unless the user has edited the Excerpt field.
function PopulateExcerptField() {
    $(document).ready(function() {
        var dirt = '<span id="ms-rterangecursor-start" rtenodeid="1"></span><span id="ms-rterangecursor-end"></span><br>';
        var OldExcerpt = $("div[role='textbox'][id^='Excerpt_']").html().split(dirt).join('');

        $("div[role='textbox'][id^='Body_']").on('blur',function() {
        var excerpt = $("div[role='textbox'][id^='Excerpt_']");

            if(excerpt.html() == OldExcerpt) {
                var firstParagraph = $(this).children('p').first().html().split(dirt).join('');
                excerpt.html('<p>' + firstParagraph + '</p>');
                OldExcerpt = excerpt.html();
            }
        });
    });
}

// Load jQuery if it doesn't already exist
(function() {

    if(typeof jQuery === 'undefined') {
        var headTag = document.getElementsByTagName("head")[0];
        var jqTag = document.createElement('script');
        jqTag.type = 'text/javascript';
        jqTag.src = '//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js';
        jqTag.onload = PopulateExcerptField;
        headTag.appendChild(jqTag);
    } else {
        PopulateExcerptField();
    }
})();

</script>

Also available as a Github Gist.

  1. Save the above gist as "PopulateExcerptField.html" and upload it to the Site Assets library within SharePoint.
  2. Navigate to your blog and go to the Create a Post page.
  3. Edit the page and add a new "Content Editor" web part, which is found under the Media and Content folder.
  4. Hover over the upper-right corner of the Content Editor web part until you see a small triangle or arrow, click on it, and choose Edit Web Part.
  5. Enter the following into the Content Link field: "/SiteAssets/PopulateExcerptField.html"
  6. Click Apply, then in the SharePoint ribbon under the Page tab click Stop Editing.
  7. (Optional) Repeat for the Edit a Post page.

Note that this code relies on JQuery, so I'm loading it from a CDN if it doesn't already exist. There are better ways to include javascript libraries in SharePoint, but that's beyond the scope of this article.

Step 3: Display the excerpts on your blog

So, our posts now have excerpts, and we've made our content authors' lives easier by adding some automation to the post creation process. But we still need a way to display them to our visitors. This is where things get complicated. Fret not. I'll take you through it step by step.

Excerpts = {
    FirstPostID: -1,
    LastPostID: -1,

    // Instead of the contents of the Body field, returns a temporary placeholder that we can easily find and replace.
    // Also finds and stores the lowest and highest post IDs for later use.
    ReplaceBody: function(ctx) {
        var id = parseInt(ctx.CurrentItem.ID);

        if(id < Excerpt.FirstPostID || Excerpt.FirstPostID === -1) {

            Excerpt.FirstPostID = id;
        }

        if(id > Excerpt.LastPostID) {

            Excerpt.LastPostID = id;
        }

        var ret = "<p id='excerpt_placeholder_" + id + "'>Loading...</p><p><a href='" + _spPageContextInfo.webServerRelativeUrl + "/Lists/Posts/Post.aspx?ID=" + id + "'>Read More</a></p>";

        return ret;
    },

    // Fetches the excerpts and replaces the placeholder elements with them.
    ReplaceExcerptPlaceholder: function() {
        $(document).ready(function() {
            $.ajax({
                url: _spPageContextInfo.webServerRelativeUrl + "/_api/web/lists/getByTitle('Posts')/items?$select=ID,Excerpt&$filter=ID le " + Excerpts.FirstPostID + " and ID ge " + Excerpts.LastPostID,
                type: "GET",
                headers: {
                    "accept": "application/json;odata=verbose",
                },
                success: function (data) {
                    var res = {};
                    for (var i = 0; i < data.d.results.length; i++) {
                        res = data.d.results[i];
                        $('#excerpt_placeholder_' + res.ID).replaceWith(res.Excerpt);
                    }
                },
                error: function (err) {
                    console.log(JSON.stringify(err));
                }
            });
        });
    }
};

// Override the Body View
(function () {
    var override = {};
    override.Templates = {};
    override.Templates.Fields = {
        'Body': {
            'View': Excerpts.ReplaceBody
        }
    };
    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(override);
})();

// Load jQuery if it doesn't already exist
(function() {
    if(typeof jQuery === 'undefined') {
        var headTag = document.getElementsByTagName("head")[0];
        var jqTag = document.createElement('script');
        jqTag.type = 'text/javascript';
        jqTag.src = '//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js';
        jqTag.onload = Excerpts.ReplaceExcerptPlaceholder;
        headTag.appendChild(jqTag);
    } else {
        Excerpts.ReplaceExcerptPlaceholder();
    }
})();

Also available as a Github Gist.

I should tell you what this code does before I explain how to use it, so let's start at the bottom and work our way up. As before, the code relies on JQuery, and I want you to be able to drop this into SharePoint as-is, so lines 121-144 load JQuery dynamically if you don't already have it.

In order to display the excerpts, we need somewhere to put them, so in lines 100-119 we register our own function to override the Body field's view for each post. I've named the function "ReplaceBody", and it's defined on lines 31-53. ReplaceBody is given a Render Context object, and is expected to return an html string. The Render Context object, ctx, contains a bunch of things, but we really only care about ctx.CurrentItem, which, as the name implies, is the SharePoint item to be rendered. It contains all of the fields exposed by the current list view, but we only need the item's ID. There are a couple of if statements that are used to store the lowest and highest post IDs for later. The function then generates an html string that contains a "<p>" element to serve as a placeholder for the excerpt and a Read More link that points to the view post page.

Finally, we have the ReplaceExcerptPlaceholder function, which is defined on lines 55-96. This function uses JQuery to asynchronously fetch the excerpts from the Posts list via SharePoint's REST API. We use the $select query to specify that we only want the ID and Excerpts fields, and the $filter query to specify that we only care about the posts whose IDs are between the lowest and highest IDs we saved previously. This helps to ensure that the returned response is as small as possible for the sake of efficiency. If we get a successful response, we iterate over it and replace each placeholder element with the actual excerpt. If something goes wrong, we log the error to the javascript console and move on.

In order to use this code, we first have to get it into SharePoint. Here's how:

  1. Save the above gist as "DisplayBlogExcerpts.js".
  2. Navigate to your site collection's Master Page Gallery, e.g. "{yoursitedomain}/_catalogs/masterpage/Forms/AllItems.aspx".
  3. In the Files tab choose Upload Document.
  4. Select DisplayBlogExcerpts.js and click OK.
  5. Set the Content Type to Javascript Display Template.
  6. Enter DisplayBlogExcerpts as the name, and optionally add a title and description.
  7. Set Target Control Type to View.
  8. Set Standalone to Standalone.
  9. Set Target Scope to "/".
  10. Set Target List Template ID to "301".
  11. In the SharePoint ribbon under the Edit tab click Save.

Now, we need to attach it to the appropriate web parts. Navigate to your blog subsite's home page, one of its category pages, and one of its archive pages, and follow the steps below for each page:

  1. Edit the page.
  2. Hover over the upper-right corner of the Posts web part until you see the small triangle or arrow, click it, then choose Edit Web Part.
  3. On the web part editor, click Miscellaneous to expand it.
  4. In the JS Link field, enter "~sitecollection/_catalogs/masterpage/DisplayBlogExcerpts.js" and click Apply.
  5. For the archive and category pages, under List Views, change Selected View to "<Summary view>" and click Apply, then change Selected View to "<Current view>" and click Apply again.
  6. In the SharePoint ribbon under the Page tab click Stop Editing.

If everything worked, you should now have excerpts on all of your blog's list pages.

Final Thoughts

As mentioned before, there are other ways to accomplish the same thing. You could use CSOM instead of the REST API to fetch the Excerpt fields. I find the REST API easier to work with, but this is a matter of developer preference. You can expose the Excerpt field directly in the blog web part's list view, though this doesn't work for the archive or category pages. You could even roll your own blog list web part, as we recently did for one of our clients.

My goal with this article was to provide a ready-made solution that you can drop into an existing SharePoint site and have it "just work". Best of all, the code is MIT licensed, so use and modify to your heart's content.

Dustin takes a methodical approach to creating software. His attention to detail is exemplar, and he's always willing to advocate for new technologies, like Meteor.

Fynydd is a software design and development company that creates awesome user experiences for all kinds of devices. Our expertise has helped people to do things like:

  • Create a new product or service.
  • Automate processes and workflows.
  • Bring a mobile strategy to life.
  • Better interact with their customers.

How can we help you? Contact us and let us know.