Adding Social Sharing to a Web Site

Adding Social Sharing to a Web Site

How I built a social sharing component for my own web site and added a secured geo-located audit trail. Step by step, let’s analyse technologies and source code for developing this component.

 

This is the first of two articles about adding social sharing capability to a web site, and auditing its usage. In this first article I’ll focus on building the sharing functionality; in the second article I’ll present a secured geo-located audit trail.

 

Introduction

There are many available components for adding social sharing capability to a web site, that is the possibility to share a specific page on the like of Facebook, Twitter, LinkedIn, etc. However, I decided to implement my own component for my blog, in order to achieve a specific look & feel that I wanted, and obviously to gain more experience on how to deal with the several social network providers.

At time of writing this article, I have implemented the sharing functionality on the following social network sites:

·         Facebook

·         Twitter

·         Google+

·         LinkedIn

·         Pinterest

 

I have also decided to add a “Share by Email” option, for those like me who like to send emails to themselves with URLs of web pages to read (one day when I will have spare time…). In addition, when sharing the page, an audit trail is generated, capturing information about the client. A significant piece of information is the IP address of the referrer, i.e. the client browser. From this IP address, I can locate the country of origin of the request, for purely statistical pleasure J

I took a top-down approach in designing this component, or as I prefer to say, a UXF (User Experience First) approach. In a nutshell:

·         I designed the component using a mix of Bootstrap for responsive design, and have added an Entypo font for the social glyphicons.

·         I added the component to an MVC view and connected it to an MVC controller with an Ajax call for the auditing part; I could have used a WebAPI, but I preferred to stick to simplicity of execution in this case. The auditing action is secured using a Triple DES-encrypted security token.

·         Once the audit information is stored in the database (Entity Framework takes care of the data access layer), I used a third-party service for resolving the geolocation of the client request, based on its IP address.

The end result visible to the user looks like this (and you can see on this web page too – feel free to use it!):

 

The Container View

As said, the component is implemented in a MVC view, specifically a Razor partial view.

From a look & feel perspective, I like minimalist layouts, well-spaced and with simple lines. All the sharing buttons are implemented as Bootstrap pills. In my case they are displayed horizontally and justified within the parent container, but this can be easily changed to accommodate different layouts.

But before getting into the implementation details of the component itself, we need to look at the page that hosts the sharing button. For the sake of this article, all we need is a simplified Article model, implemented in a POCO class as follows:

public class Article

{

    public int Id { get; set; }

 

    [StringLength(200)]

    [Required, Index(IsUnique = true)]

    public string Title { get; set; }

 

    [DataType(DataType.MultilineText)]

    public string Abstract { get; set; }

}

The Article model.

 

An article is uniquely identified by the Id property. This is convenient for Entity Framework to save the article into a database table, having the Id as identity. There is a required title, which is also indexed to be unique (that is, no two articles can have the same title), and an optional abstract. There should be additional properties for content, format, creation date, publish date, etc. but this is off topic for the purpose of the sharing buttons component.

The solution attached to this article has an Articles controller that is generated with the default scaffolding of an MVC 5 controller with EF CRUD operations: Index, Details, Edit, Create and Delete. I won’t go into the details of this controller, this is purely auto-generated code in Visual Studio. All I need this controller for is to provide a backend mechanism for entering articles in the application.

The actual display of an article is done by the Default controller, which is the controller that is executed when running the application. The Index action represents the “home page” of the application, which is based on a shared layout (_Layout.cshtml) and content defined in the Index.cshtml view. This is all very standard ASP.NET MVC. I am mentioning this as an opportunity to define my library dependencies, all managed via NuGet, obviously. The list of referenced libraries, server and client side, is in the following picture:

List of referenced libraries in the project.

 

All these libraries are automatically added to the solution when created the first time, and Entity Framework added when a controller with EF actions is generated, except for Microsoft jQuery Unobtrusive Ajax. This is a plugin for jQuery that is used by the MVC Ajax helper for invoking actions on a controller via Ajax, that is without post back of the entire page to the server. Convenient, as we will see later, for the audit trail tracking.

Back to our home page, the Index action in the Default controller simply retrieves a list of available articles ordered alphabetically by title, and passes it to the Index view.

public ActionResult Index()

{

    using (var db = new DatabaseContext())

    {

        return View(db.Articles.OrderBy(a => a.Title).ToList());

    }

}

The Index action in the Default controller.

 

The view accepts an enumerable of Article and displays them in an unordered list:

<ul>

@foreach (var item in Model)

{

    <li>@Html.ActionLink(item.Title, "Open", new { id = item.Id })</li>

}

</ul>

List of articles in the Index view.

 

Not particularly fancy and sophisticated UI, I know J but it gets to the point immediately!

As you notice in the above code, each article is displayed in a list item as a link (<a> tag generated by the Html.ActionLink helper). This is a link to the Open action of the same Default controller, which, as you may guess from the name, simply “opens” the article, i.e. displays its content as a web page.

The Open action is defined as follows:

public ActionResult Open(int id)

{

    using (var db = new DatabaseContext())

    {

        return View(db.Articles.Find(id));

    }

}

The Open action in the Default controller.

 

This action finds the specific article by Id in the database context, and passes it on to the view. The view, in turn, displays the article as a web page.

<h2>@Model.Title</h2>

<p>

    @Model.Abstract

</p>

 

<hr />

 

@Html.Action("ShareButtons", "Social", new { id = Model.Id })

 

The very important line of code in this extremely simple view is the last one, the Html.Action call to the ShareButtons action in the Social controller.

This is one of the way, and probably my preferred one, to include reusable components in a web page. By creating self-contained partial views with all the resources included (HTML, CSS and potentially JavaScript code) in a single separate file, it is extremely easy to embed that partial view into a parent view. Html.Action fits exactly the purpose, allowing to call an action on a controller, getting HTML code in return, and then displaying that code inline within the container page.

 

The ShareButtons Action

The share buttons are implemented in the ShareButtons action and view. I have introduced a dedicated MVC controller for the purpose, to keep action isolated. The Social controller contains the actions for displaying the view and auditing the use of the share buttons.

[ChildActionOnly]

public ActionResult ShareButtons(int id)

{

    using (var db = new DatabaseContext())

    {

        return PartialView(new

        {

            Article = db.Articles.Find(id),

            SecurityToken = Request.Url.Host.Encrypt(EncryptionKey, EncryptionIV)

        }.ToDynamic());

    }

}

The ShareButtons action in the Social controller.

 

This action performs a few interesting things:

1.     It takes the article Id as input parameter and finds the article in the database context.

2.     It generates a security token to pass to the view for validating subsequent Ajax calls for auditing purpose.

3.     It puts together the Article and the Security Token into an anonymous object, but because anonymous objects cannot be passed to an MVC view, it uses the ToDynamic() extension to convert the anonymous object into a dynamic object (more precisely, into an ExpandoObject).

Let’s expand on the need for a dynamic model. I’m discussing the securing auditing in the second part of this article.

 

From Anonymous to Dynamic

The ToDynamic extensions used in the ShareButtons action converts an anonymous object to a dynamic object, more precisely an ExpandoObject. This is necessary because it is not possible to pass an anonymous object directly to a view, so I need to pass through a dynamic object… but hold on, why do we need an anonymous object at all? Right, good question! J

I need this data in the component view:

·         Id of the article

·         Title of the article

·         URL of the web page

·         The security token to secure auditing

Id and Title of the article are used for auditing and sharing, respectively. The best way to pass this is in the Article object itself, as they are already there. So my model, at the moment, would likely be the Article class.

However, I also need the URL of the article page, and the security token. The URL is also used for sharing by the buttons (that’s exactly what we are sharing, the URL of the article!). But this is easy to obtain from the client request: Request.Url.AbsoluteUri.

The security token would also need to be passed to the view. Now, there are different ways of passing values from a controller to a view, there are different schools of thought about each way, and all of them are valid and legitimate. So I simply have to pick one!

1.     Using ViewBag: I could pass the Article as the model of the view, and then transfer the security token via the ViewBag.

2.     Using a View Model: I could create a new class to contain exactly what I need, an Article and a SecurityToken property; this would be a bespoke class strongly tied to the view with very little reusability, but perfectly valid for the purpose.

3.     I have decided to instantiate a new anonymous object containing two properties, Article and SecurityToken; this object is then converted to an ExpandoObject for the mentioned reasons, and then passed to the view.

This last approach has a better object-oriented flavour, for my like, as I could add other properties directly to the anonymous object as needed, and without modifying a separate class, as it would be in case of the approach with a View Model. As for the ViewBag, that’s perfectly fine but properties are not strongly typed, so when using it in the view to access the article Id and Title, I’d need to cast to the Article class first. This creates an unnecessary dependency between the view and the model.

So anonymous approach it is! Let’s see how to convert from anonymous to dynamic:

1.     Create an instance of an ExpandoObject.

2.     For each public property defined at instance level in the anonymous object, add the property name and value combination to the expando object as key and value; this is possible because the ExpandoObject class implements the IDictionary<string, object> interface.

The source code is:

public static ExpandoObject ToDynamic(this object data)

{

    dynamic expando = new ExpandoObject();

 

    foreach (PropertyInfo property in data.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))

    {

        if (property.CanRead)

        {

            (expando as IDictionary<string, object>).Add(property.Name, property.GetValue(data, null));

        }

    }

 

    return expando;

}

Extension to convert from anonymous object to dynamic.

 

The ShareButtons View

The ShareButtons view is where the layout of the component is implemented, and the code for sharing on the social networks and auditing the action. It is technically a partial view, which means that no layout is used, as the component is embedded in the hosting page.

There are a lot of elements in this page… so let’s go in order. First of all, like any good MVC view, it should have a strongly typed model associated. As discussed previously, our model is a dynamic object that contains two properties, Article and SecurityToken. So the very first line of code of the ShareButton.cshtml view is indeed the declaration of the model.

@model dynamic

 

Then I need to encode the article title and the page URL to pass this information to each share button, as we will see soon. So let me allocate two local string variables with the URL-friendly title and URL of the article.

@{

    string encodedTitle = Uri.EscapeDataString(Model.Article.Title);

    string encodedUrl = Uri.EscapeDataString(Request.Url.AbsoluteUri);

}

 

Now the UI of the component. I am using Bootstrap pills for the buttons, the Entypo Social font for the icons, a bespoke CSS stylesheet, jQuery for event handling, Bootstrap modal for email sharing, Ajax for auditing… lot of stuff! Ok, in order then, the buttons are “pills”, the code below is for the “Share on Facebook” button. The other buttons are all similar, except for the “Share by Email” as we will see in a moment.

<ul class="nav nav-pills nav-justified" data-id="@Model.Article.Id">

    <li>

        <a class="social social-facebook"

        data-social="Facebook" data-src= "https://www.facebook.com/sharer/sharer.php?u=@(encodedUrl)&t=@(encodedTitle)&display=popup"

        title="Share on Facebook">

            <span class="entypo-social facebook"></span>

       </a>

    </li>

    . . .

</ul>

 

The Razor code is self-explanatory, I’m passing the encoded URL and the encoded title to the sharer service of Facebook, in this case. Each social network site has its own syntax for sharing, but they all look very similar. A particular mention needs the assignment of the article id in the data-id attribute of the navigation list. This will be used later for the auditing action. There is large use of “data” attributes because unobtrusive JavaScript is bound to the “social” class to respond to the “click” event.

$(function () {

    $(".social").on("click", function () {

        var src = $(this).data("src");

 

        if (social != "Email") {

            window.open(src, "SocialSharing", "<windows options>", true);

        }

    });

});

Share button On Click event handler

 

A new window is opened with the indicated URL taken from the data-src attribute, but only if the pressed button is not for sharing by email, because it that case the functionality is slightly different.

The icons for each share buttons are glyphicons. For the social network site, we use the Entypo Social font, whereas for email sharing, the Bootstrap glyphicon-envelope will suffice.

In order to use the Entypo Social font, the following font types should be added to the project and referenced in the Social.css stylesheet.

Entypo Social fonts along with the Bootstrap Glyphicons.

 

The CSS file defines a new font-face directive, and a CSS class “entypo-social” that references it. For each of the social network sites used in this sharing component, a sub-class is defined to be used in the HTML code for each character of the font representing the icon of the social network site.

@font-face {

    font-family: 'EntypoSocialRegular';

    src: url('../fonts/entypo-social.eot');

    src: url('../fonts/entypo-social.eot?#iefix') format('embedded-opentype'),

         url('../fonts/entypo-social.woff') format('woff'),

         url('../fonts/entypo-social.ttf') format('truetype'),

         url('../fonts/entypo-social.svg#EntypoRegular') format('svg');

    font-weight: normal;

    font-style: normal;

}

 

.entypo-social {

       font-family: 'EntypoSocialRegular';

       font-size: 2em;

       font-weight: normal;

       line-height: 0;

}

 

.entypo-social.twitter:before { content:'\F309'; }

.entypo-social.facebook:before { content:'\F30C'; }

.entypo-social.googleplus:before { content:'\F30F'; }

.entypo-social.pinterest:before { content:'\F312'; }

.entypo-social.linkedin:before { content:'\F318'; }

CSS for the Entypo Social font.

 

Sharing by Email

For sharing by email, a Bootstrap modal is used, instead. The body of the modal contains a form for entering email address and body of the email. The body is prefilled with the article’s title and URL, but it can be edited.

Share by Email form.

 

When sending the email, the form is submitted back to the ShareByEmail action in the Social controller. Submission is actually done via Ajax and the form is declared using the Ajax.BeginForm helper, which requires the Microsoft jQuery Unobtrusive Ajax library referenced via NuGet.

@using (Ajax.BeginForm("ShareByEmail", "Social", new AjaxOptions { HttpMethod = "POST", ... }))

 

The ShareByEmail action responds only to POST requests, validates the anti-forgery token, and accepts input parameters as a form collection. Then, it sends the email using MailMessage and SmtpClient in the System.Net.Mail namespace.

[HttpPost]

[ValidateAntiForgeryToken]

public ContentResult ShareByEmail(FormCollection values)

{

    string emailAddress = values["email_address"];

    string emailSubject = values["email_subject"];

    string emailBody = values["email_body"];

    . . .

Portion of the ShareByEmail action. Full source code in the project online.

 

SMTP details are configured in the appSettings section of the Web.config file:

<appSettings>

    <add key="SmtpServer" value="[SMTP SERVER]"/>

    <add key="SmtpUser" value="[SMTP USER]"/>

    <add key="SmtpPassword" value="[SMTP PASSWORD]"/>

</appSettings>

 

And referenced by a strongly typed constants using the new C# 6 syntax for properties:

public static string SmtpServer => System.Configuration.ConfigurationManager.AppSettings["SmtpServer"];

 

Conclusions

The sharing buttons are all developed and fully functional, you can find the full source code of the solution on CodePlex. Feel free to use it, it’s completely open source!

In the next article I will describe the implementation of a secured geo-located auditing capability, to track usage of the sharing buttons.

 


  Comments

 

 Source Code

Project Name: SocialSharing


 Related Content
A flexible Default Value for your DateTime properties
When creating an MVC application with Entity Framework, it is possible to set default values for most properties in a model using the DefaultValue attribute. However, no much flexibility is offered for a DateTime property. This article presents a custom validation attribute for DateTime types that accepts different formats for defining the default value of the property.
Adding a Secured Geo-located Audit Trail
How I built a social sharing component for my own web site and added a secured geo-located audit trail. Step by step, let’s analyse technologies and source code for developing this component.
Best practices for mobile form design in SharePoint
Build effective SharePoint forms with Nintex that are accessible anywhere, at any time, and on any device. You built the workflows, you built the forms, now make them mobile.
Bring your “A” game to ESPC16
With just over 3 weeks to go to Europe's largest gathering of SharePoint & Office 365 professionals, take a look at these tips that will help you get the most out of ESPC16…
Building an MVC application for SharePoint
Learn how to write code to perform basic operations with the SharePoint 2013 .NET Framework client-side object model (CSOM), and build an ASP.NET MVC application that retrieves information from a SharePoint server.
CIO vs CTO
What are the synergies and differences of the roles of a Chief Information Officer and a Chief Technology Officer? An open conversation about two roles with one mission…
Coded UI test automation of MVC applications with Visual Studio
Whether you are a software developer, tester, administrator or analyst, this article can help you master different types of UI testing of an MVC application, by using Visual Studio for creating coded UI test suites that can be automated for continuous execution.
Converting GIS spatial coordinates
Different formats and standards exist for describing geographical coordinates in GIS systems and applications. This article explains how to convert between the most used formats, presenting a working library written in C#.
Creating mobile accessible forms in SharePoint
With the release of the Nintex Mobile apps, SharePoint users can now optimise their experience across popular mobile devices and platforms.
Define your Performance Testing strategy with Visual Studio
Performance Testing is an essential part of software testing, with the specific goal of determining how a system performs in terms of responsiveness and stability under a particular workload. In this series of posts we’ll define and execute a good strategy for testing performance of an application using Visual Studio.
Disserting about colliding GUIDs and the Big Bang Theory
Can you generate two identical GUIDs? Would the world end if two GUIDs collide? How long does it take to generate a duplicate GUID and would we be still here when the result is found?
GIS Location Services in Dynamics CRM
A design paper about implementing GIS-based services for a local Council in Dynamics CRM, structuring address data, and delivering location services in the form of WebAPI endpoints via an Enterprise Service Bus.
Group or Team?
All teams are groups but not all groups are teams. What defines a group and what a team? When do we need one over the other one?
How to Give Constructive Feedback
Learning to give and receive constructive feedback is an essential part of learning, growing, improving and achieving our goals.
Mirroring an iPad on your laptop
Have you ever wanted to see your iPhone or iPad on a larger screen? Play games, watch movies, demo apps or present to your computer from your iPhone or iPad. Reflector mirrors iOS devices on big screens wirelessly using iOS built-in AirPlay mirroring.
Mobilize your SharePoint workflows
Build workflow applications in SharePoint that can be accessed on mobile devices using the Nintex solution for business process mobilization.
Natural String Sorting
Have you ever desired to have in your code a way to order a sequence of strings in the same way as Windows does for files whose name contains a mix of letters and numbers? Natural string sorting is not natively supported in .NET but can be easily implemented by specialising a string comparer and adding a few extensions to the enumerable string collection.
Sales Effectiveness in Dynamics CRM with Azure IoT and Machine Learning - Part 1
How can an organisation optimise its sales channels and product targeting by building a 365-degree view of its customers in Dynamics CRM? The answer, and topic of this article, is with the help of Azure IoT and Machine Learning services!
Scaling Applications with Azure Redis and Machine Learning - Part 1
This article presents design best practices and code examples for implementing the Azure Redis Cache and tuning the performance of ASP.NET MVC applications, optimising cache hit ratio and reducing “miss rate” with smart algorithms processed by Machine Learning.
Software Development Management in 11 steps
What it takes to be a great Software Development Manager? I have been building software for the last 15 years and have collected a few stories and experiences to share. Some I have used as questions when interviewing candidates. In 11 points, this is my story to date.
SOLID SharePoint apps with MVC
Practical code examples of ASP.NET MVC applications that connect to a SharePoint Server and comply with the SOLID principles.
The art of outsourcing projects
Outsourcing may look financially attractive, but working with companies in far-off lands introduces challenges that, if not considered properly, can drive any project to failure. Let’s explore some common pitfalls when working with offshore partners and a common-sense approach to work around them.
The value of hashtags
Customers expect a modern approach to advertising. Digital advertising can leverage evolving technology to provide just-in-time, just-at-the-right-place promotions.
We don't need no Yoda's syntax
There is an urban myth in the programmers’ community that the so called “Yoda’s syntax” performs better when checking an object for nullity. Let's demystify it...