Display your SharePoint blog posts in a SP Web Part

The built-in blog tool in SharePoint 2010 and SharePoint 2007 is a basic blog tool with everything you need to quickly share ideas and information in your organization. The blog template exists in all SharePoint versions since 2007 which includes all server versions as well as the free wss and Foundation distributions.

A quick research shows that a common request is to show the SharePoint blog posts on multiple SharePoint sites and possibly on external web sites, but lets talk about that in another blog post.

The bad news is that this type of internal integrations quickly becomes very complex and proprietary using CAML, XSLT and the built-in SharePoint Class Libraries and WS References (have a look)

The good news is that when using the Camelot .NET Connector for SharePoint we can build a very versatile and modular web part with just a few functions.

SharePoint blog site basics

The SharePoint blog is a site template that contains lists and libraries, such as a list of blog posts, a list of other blogs (SharePoint 2007 only), and a library for photos. Once you create a blog, you can set up categories, and then customize the blog settings.

Available lists

  • Categories
  • Comments
  • Links
  • Other Blogs (SharePoint 2007 only)
  • Photos
  • Posts

The design

Instead of pushing the posts to lists all over the sites we will build a web part who is able to fetch blog posts filtered by their category. One simple but important design difference between the SharePoint 2007 and 2010 versions is that in the 2010 versions you are able to select multiple categories to a blog post.

Wish list

The design specification tells me that it should be a simple and modular web part who can

  • Connect to any SharePoint installation (which we can reach) and fetch blog posts
  • Support both SharePoint 2007 and 2010
  • Fetch blog posts from a single or multiple categories, no category selected should fetch all posts
  • Total posts to fetch
  • Decide sort order
  • Display blog titles only, the titles must have a href link to the actual post
  • Specify how many words to display from retrieved posts
  • Display a read more link in the end of each excerpt.

Thats not much is it?

You can read more and download the source and
binaries for this web part here

The code

Requirements

/***************************************
 * Camelot "Camelot SP Blog Reader" for SharePoint
 * @version 1.0
 * @author Bendsoft
 * @package Camelot SP Blog Reader 
 * @subpackage Camelot
 * @license FreeBSD License

 * Bendsoft 2011 
 * www.bendsoft.com
***************************************/


using System;
using System.ComponentModel;
using System.Data;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using Camelot.SharePointConnector.Data;

namespace CamelotSPBlogReader
{

    public class CamelotSPBlogReader : System.Web.UI.WebControls.WebParts.WebPart
    {
        //Microsoft.SharePoint.WebPartPages.WebPartPage
        // Set up connection to SharePoint
        private string connectionString = string.Empty;
        private SharePointConnection conn;
        private System.Version v = Microsoft.SharePoint.Administration.SPFarm.Local.BuildVersion;

        #region ConnectionString

        private string serverAddress;
        [Category("ConnectionString"),
        WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        WebDisplayName("Server address"),
        WebDescription("The IP or hostname of the SharePoint server. Required.")]
        public string ServerAddress
        {
            get
            {
                if (serverAddress != null)
                    return serverAddress;
                else
                    return "localhost";
            }
            set { serverAddress = value; }
        }

        private string userName;
        [Category("ConnectionString"),
        WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        WebDisplayName("User name"),
        WebDescription("User name of the account used to access SharePoint. This account must have sufficient privileges to access the provided site. Required when server uses authentication mode [Ntlm] or [Basic].")]
        public string UserName
        {
            get
            {
                if (userName != null)
                    return userName;
                else
                    return string.Empty;
            }
            set { userName = value; }
        }

        private string userPassword;
        [Category("ConnectionString"),
        WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        WebDisplayName("Password"),
        WebDescription("Password for the actual user")]
        public string UserPassword
        {
            get
            {
                if (userPassword != null)
                    return userPassword;
                else
                    return string.Empty;
            }
            set { userPassword = value; }
        }

        private string userDomain;
        [Category("ConnectionString"),
        WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        WebDisplayName("User Domain (optional)"),
        WebDescription("The domain of the specified user. Leave empty if domain user account is not used.")]
        public string UserDomain
        {
            get
            {
                if (userDomain != null)
                    return userDomain;
                else
                    return string.Empty;
            }
            set { userDomain = value; }
        }

        private string siteName;
        [Category("ConnectionString"),
        WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        WebDisplayName("Site Name"),
        WebDescription("Corresponds to the site path on the SharePoint server, e.g. marketing or marketing/news. Leave empty for top site.")]
        public string SiteName
        {
            get { return siteName; }
            set { siteName = value; }
        }

        private string authentication;
        [Category("ConnectionString"),
        WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        WebDisplayName("Authentication"),
        WebDescription("Indicates the authentication mode used on the SharePoint server. [Default]: Use the credentials from the current security context. No specific account details should be provided. Only set this mode when SPC is used in components running inside SharePoint, such as webparts. SPC is then running as the current user. [Ntml]: Specifies client authentication using Ntml. This is the most common scenario. [Basic]: Specifies that user authenticates to the server using basic authentication which is a simpler less secured form.")]
        public string Authentication
        {
            get
            {
                if (authentication != null)
                    return authentication;
                else
                    return "Default";
            }
            set { authentication = value; }
        }

        #endregion

        #region Property Blog feed settings

        public enum SelectVersionEnum { Version2010, Version2007 };
        protected SelectVersionEnum selectVersion;
        [Category("SharePoint Blog Settings"),
        WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        WebDisplayName("SharePoint Version"),
        WebDescription("SharePoint version where the blog is kept. Version2010 means SharePoint 2010 or newer, Version2007 means SharePoint 2007 or older.")]
        public SelectVersionEnum SelectVersion
        {
            get { return selectVersion; }
            set { selectVersion = value; }
        }

        private string blogCategory;
        [Category("SharePoint Blog Settings"),
        WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        WebDisplayName("Category/Categories"),
        WebDescription("Type category/categories to fetch, separate with semicolon.")]
        public string BlogCategory
        {
            get { return blogCategory; }
            set { blogCategory = value; }
        }

        private int posts;
        [Category("SharePoint Blog Settings"),
        WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        WebDisplayName("No. posts"),
        WebDescription("How many posts to fetch")]
        public int Posts
        {
            get
            {
                if (posts != 0)
                    return posts;
                else
                    return 5; // Let's have 5 as default!
            }
            set { posts = value; }
        }

        public enum TitlesByEnum { FALSE, TRUE }; // Set default value first!
        protected TitlesByEnum titlesOnly;
        [Category("SharePoint Blog Settings"),
        WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        WebDisplayName("Show titles only"),
        WebDescription("If TRUE the control only renders the title link.")]
        public TitlesByEnum TitlesOnly
        {
            get { return titlesOnly; }
            set { titlesOnly = value; }
        }

        public enum SortByEnum { DESC, ASC }; // Set default value first!
        protected SortByEnum sortOrder;
        [Category("SharePoint Blog Settings"),
        WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        WebDisplayName("Sort order"),
        WebDescription("ASC = Oldest post first, DESC = Newest post first")]
        public SortByEnum SortOrder
        {
            get { return sortOrder; }
            set { sortOrder = value; }
        }

        private int wordCount;
        [Category("SharePoint Blog Settings"),
        WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        WebDisplayName("No. words"),
        WebDescription("How many words to display, 0 will render the entire post.")]
        public int WordCount
        {
            get
            {
                return wordCount;
            }
            set { wordCount = value; }
        }

        public enum ShowByEnum { TRUE, FALSE }; // Set default value first!
        protected ShowByEnum showReadMore;
        [Category("SharePoint Blog Settings"),
        WebBrowsable(true),
        Personalizable(PersonalizationScope.Shared),
        WebDisplayName("Show read more link"),
        WebDescription("If TRUE the control renders a read more link.")]
        public ShowByEnum ShowReadMore
        {
            get { return showReadMore; }
            set { showReadMore = value; }
        }

        #endregion

        /// <summary>
        /// Base method to construct the web part
        /// </summary>
        /// <returns></returns>
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            try
            {
                /** Set up connection to your SharePoint server **/
                connectionString = "Server=" + ServerAddress + ";Database=" + SiteName + ";Domain=" + UserDomain + ";User=" + UserName + ";Password=" + UserPassword + ";Authentication=" + Authentication + ";TimeOut=60;decodename=true";
                conn = new SharePointConnection(connectionString);

                /** Use the buildCategoryQuery to build the conditional query for selected categories **/
                string sqlCategories = buildCategoryQuery();

                /** Build query for Posts list **/
                string sqlLimit = (posts > 0) ? string.Format(" LIMIT {0}", posts) : "";
                string sqlSort = string.Format(" ORDER BY PublishedDate {0}", sortOrder);

                string sql = (titlesOnly.ToString() == "TRUE") ?
                    string.Format("SELECT Title, ID FROM `Posts` {0} {1} {2}", sqlCategories, sqlSort, sqlLimit) :
                    string.Format("SELECT Title, Body, ID FROM `Posts` {0} {1} {2}", sqlCategories, sqlSort, sqlLimit);
                SharePointDataAdapter adapter = new SharePointDataAdapter(sql, conn);

                DataTable dt = new DataTable();
                adapter.Fill(dt);

                /** Output **/
                foreach (DataRow row in dt.Rows)
                {
                    if (titlesOnly.ToString() == "TRUE")
                    {
                        // Header
                        Controls.Add(new LiteralControl(string.Format("<h2 class=""><a href="/{0}/Lists/Posts/Post.aspx?ID={1}">{2}</a></h2>", SiteName, row["ID"], row["Title"])));
                    }
                    else
                    {
                        // Header
                        Controls.Add(new LiteralControl(string.Format("<h2 class=""><a href="/{0}/Lists/Posts/Post.aspx?ID={1}">{2}</a></h2>", SiteName, row["ID"], row["Title"])));

                        // Body
                        Controls.Add(new LiteralControl(string.Format("<p class="">{0}</p>", truncateAtWord(row["Body"].ToString()))));

                        // Read More link
                        if (showReadMore.ToString() == "TRUE")
                            Controls.Add(new LiteralControl(string.Format("<a class="" style="text-align: right;" href="/{0}/Lists/Posts/Post.aspx?ID={1}">Read more</a>", SiteName, row["ID"])));
                    }
                }
            }
            catch (Exception a) { Controls.Add(new LiteralControl(a.Message)); }

        }

        /// <summary>
        /// Builds a partial SQL string to match values in the Post list.
        /// </summary>
        /// <returns></returns>
        private string buildCategoryQuery()
        {

            string sqlCategories = string.Empty;
            string sqlWhere = string.Empty;
            string sqlReturn = string.Empty;

            if (!string.IsNullOrEmpty(blogCategory))
            {

                // Handle the input
                string[] blogCategories = blogCategory.Split(new char[] { ',' });

                switch (selectVersion.ToString())
                {
                    case "Version2007":
                        // Get category id's. We should be able to query the Posts table directly (looking for CategoryWithLink) but 
                        // some SharePoint setups wont allow us to query computed values through the api, thus this small detour.
                        // This mainly applies to SharePoint 2007 or older releases

                        // Build conditions for the query in the categories table
                        for (int i = 0; i < blogCategories.Length; i++)
                        {
                            if (i < blogCategories.Length - 1)
                                // If several
                                sqlWhere += string.Format(" LinkTitle = '{0}' OR ", blogCategories[i].Trim());
                            else
                                // If single or last
                                sqlWhere += string.Format(" LinkTitle = '{0}' ", blogCategories[i].Trim());
                        }

                        // Fetch category ID's
                        sqlCategories = string.Format("select ID from categories where {0}", sqlWhere);
                        SharePointDataAdapter adapter = new SharePointDataAdapter(sqlCategories, conn);
                        DataTable dt = new DataTable();
                        adapter.Fill(dt);

                        // Build querystring
                        if (dt.Rows.Count > 0)
                        {
                            sqlReturn = " WHERE ";
                            int i = 0;

                            foreach (DataRow row in dt.Rows)
                            {
                                i++;
                                sqlReturn += string.Format(" PostCategory = {0} ", row["ID"]);

                                // Add OR if not last row
                                if (i < dt.Rows.Count)
                                    sqlReturn += " OR ";
                            }
                        }
                        break;

                    case "Version2010":
                    default:
                        // In SharePoint 2010 or newer we can query the Posts list directly with a string value 
                        // matching the values in CategoryWithLink

                        if (blogCategories.Length > 0)
                        {
                            for (int i = 0; i < blogCategories.Length; i++)
                            {
                                if (i < blogCategories.Length - 1)
                                    // If several
                                    sqlWhere += string.Format(" CategoryWithLink = '{0}' OR ", blogCategories[i].Trim());
                                else
                                    // If single or last
                                    sqlWhere += string.Format(" CategoryWithLink = '{0}' ", blogCategories[i].Trim());
                            }

                            sqlReturn = " WHERE " + sqlWhere;
                        }
                        break;

                }
            }
            return sqlReturn;

        }

        /// <summary>
        /// Cut the input string after n words
        /// </summary>
        /// <param name="input"></param>
        /// <returns>Ready-formated string</returns>
        private string truncateAtWord(string input)
        {
            try
            {
                string output = string.Empty;
                string[] inputArr = input.Split(new char[] { ' ' });

                if (inputArr.Length <= wordCount)
                    return input;

                if (wordCount > 0)
                {
                    for (int i = 0; i < wordCount; i++)
                    {
                        output += inputArr[i] + " ";
                    }
                    output += "...";
                    return output;
                }
                else
                {
                    return input;
                }
            }
            catch (Exception a)
            {
                return a.Message;
            }

        }

        /// <summary>
        /// Find and return a hint of the current SharePoint version
        /// </summary>
        /// <returns></returns>
        private string VersionInformation()
        {
            string versionReturn = string.Empty;
            if (Convert.ToInt32(v.Major) <= 12)
                versionReturn = "2007 or older.";
            else
                versionReturn = "2010 or newer.";

            return string.Format("Hint! You are using SharePoint {0}", versionReturn);
        }

    }
}

The settings dialogs will look like this

You can download the entire Visual Studio project or precompiled binary at our official web site www.bendsoft.com, the project is stored at the following url http://www.bendsoft.com/downloads/sharepoint-web-parts/sharepoint-blog-reader/

Enjoy

This entry was posted in Integrations, SharePoint, SharePoint Web Part Development. Bookmark the permalink.

One Response to Display your SharePoint blog posts in a SP Web Part

  1. Once you get past the nitty gritty, share point is an excellent tool to get information out there quickly and efficiently.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>