Blog posts by Chris Banner2017-09-01T22:08:23.0000000Z/blogs/chris-banner/Optimizely WorldAnnouncing the Episerver Social Developer Portal/blogs/chris-banner/dates/2017/9/announcing-the-episerver-social-developer-portal/2017-09-01T22:08:23.0000000Z<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>The Social team is excited to announce that we have recently launched the <a href="https://demo.social.episerver.net">Episerver Social Developer Portal</a>! </p>
<p>Through the portal, members of the Episerver development community can provision and manage trial instances of Episerver Social. An instance grants you full access to the Episerver Social platform for 30 days. Use your instances to get acclimated with the platform, create a proof of concept, or in the process of developing applications for your clients. </p>
<p>The portal is open to all members of the Episerver World community. So, we encourage you to sign up now and give Social a try!</p>
<p>(Additional details can be found on the Portal's <a href="https://demo.social.episerver.net/Home/Faq">frequently asked questions page</a>.)</p>
<p>Happy socializing!</p>
</body>
</html>What's new in Episerver Social 1.5?/blogs/chris-banner/dates/2017/7/whats-new-in-episerver-social-1-5--big-things-are-on-the-way/2017-07-10T13:14:45.0000000Z<p>This week brings the release of Episerver Social 1.5. In addition to a handful of internal enhancements and performance tweaks, this version introduces the <strong><em>AnyExpression. </em></strong></p>
<p>This new expression allows you to compare a field of your extension data against a set of values, matching documents where the field's value is represented by any within the set.</p>
<p>Consider a scenario where a collection of Comments has been extended with the following sample of extension data:</p>
<pre class="language-csharp"><code>{ LuckyNumber = 7 }
{ LuckyNumber = 42 }
{ LuckyNumber = 5 }
{ LuckyNumber = 10 }</code></pre>
<p>With the <em><strong>AnyExpression</strong></em> we can now identify those Comments with a LuckyNumber that matches any of a series of values.</p>
<pre class="language-csharp"><code>var luckyNumberField = FilterExpressionBuilder<MyCommentExtension>.Field(ext => ext.LuckyNumber);
var luckiestComments = luckyNumberField.Any(new long[] { 5, 7, 9 });
var criteria = new CompositeCriteria<Comment, MyCommentExtension>
{
ExtensionFilter = luckiestComments
};
var commentService = ServiceLocator.Current.GetInstance<ICommentService>();
var results = await commentService.GetAsync(criteria);</code></pre>
<p>In the snippet show above, the service will return a set of Comments extended with the lucky numbers 5, 7, or 9. Given our initial sample, this means that we'd receive those comments with: </p>
<pre class="language-csharp"><code>{ LuckyNumber = 7 }
{ LuckyNumber = 5 }</code></pre>
<p>This expression can be applied to any Episerver Social feature accepting extension data. For more details, feel free to dig into the <a href="/link/a09c5c7c7ca54b9b8bcb06d846cf9031.aspx#filtering_composites">developer guide</a>.</p>What's new in Episerver Social 1.4?/blogs/chris-banner/dates/2017/6/whats-new-in-episerver-social-1-4/2017-06-05T00:02:11.0000000Z<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>Episerver Social 1.4 releases this week. Below you'll find the highlights of what's new in this release. (For an individual breakdown of changes, <a href="/link/76974ad8d2a84c1b989ad0ac453ab663.aspx?versionFilter=1.4.0&packageFilter=EPiServer.Social.Framework&typeFilter=All">check out the release notes</a>.)</p>
<h2><strong>Enhanced expression library</strong></h2>
<p>Episerver Social emphasizes the ability to model your social content to meet the needs of your application. Supporting that flexibility, the Episerver Social framework allows you to define completely custom filters for your content using its expression library. With the release of version 1.4, the library of expressions has grown to include two new ones.</p>
<p><strong><span style="text-decoration: underline;">ContainsExpression</span><br /></strong>Apply the ContainsExpression to contruct a filter matching content where a collection field (an array, list, etc.) contains a specific value.</p>
<p>Consider an example where I wish to match Comments, which have been extended to include an accompanying <span style="font-family: 'courier new', courier, monospace;">Tags</span> array, by a specific tag value. I can construct a correponding filter using the ContainsExpression as demonstrated below.</p>
<pre class="language-csharp"><code>var containsSportsTag = FilterExpressionBuilder<MyCommentExtension>.Field(d => d.Tags).Contains("sports");</code></pre>
<p><span style="text-decoration: underline;"><strong>OrExpression</strong> </span><br />The framework, which previously included the ability to logically AND filters together, now also includes the ability to logically OR your filters.</p>
<p>Consider an example where I wish to match Comments, which have been extended to include an accompanying <span style="font-family: 'courier new', courier, monospace;">LuckyNumber</span> field, by those that equal 2 or 7. I can construct a corresponding filter using the OrExpression as demonstrated below.</p>
<pre class="language-csharp"><code>var luckyNumberField = FilterExpressionBuilder<MyCommentExtension>.Field(d => d.LuckyNumber);
var luckyNumberOf2Or7 = FilterExpressionBuilder<MyCommentExtension>.Or(
luckyNumberField.EqualTo(2),
luckyNumberField.EqualTo(7)
);</code></pre>
<p>These expressions, like the others in the library, can be applied in all of the Episerver Social features. (You can find a full overview of filtering by extension fields in <a href="/link/a09c5c7c7ca54b9b8bcb06d846cf9031.aspx#filtering_composites">the developer guide</a>.)</p>
<h2><strong>Sorting by mean</strong></h2>
<p>Episerver Social's Ratings feature automatically tabulates statistics for ratings that have been contributed. We're focused on progressively enhancing this feature over the coming months. In this release, a <span style="font-family: 'courier new', courier, monospace;">Mean</span> property has been introduced on the <span style="font-family: 'courier new', courier, monospace;">RatingStatistics</span> model. Additionally, statistical data can now be sorted by the mean value in either ascending or descending order.</p>
<h2><strong>Performance enhancements for filtering</strong></h2>
<p>We're perpetually focused on improving performance within the platform. Among the more prominent performance enhancements in this release are improvements in the filtering of your extension data.</p>
<h2><strong>Authentication troubleshooting</strong></h2>
<p>A failure to authenticate with Episerver Social is unlikely if your application is configured with the appropriate credentials. However, there are times when environmental factors (such as firewalls or proxies) might interfere, preventing your application from connecting with Episerver Social. Such scenarios may result in unexpected behavior. With that in mind, the SocialAuthenticationException has been enhanced with supplementary information to aid in the troubleshooting of your situation.</p>
</body>
</html>Getting Started with Comments/blogs/chris-banner/dates/2017/5/getting-started-with-comments/2017-05-17T19:34:53.0300000Z<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>Digital communities offer a myriad of ways for users to contribute content. Blog responses, product reviews, forum posts, and direct messages just to name a few. And while each of these mechanisms may serve a different purpose, they share quite a lot in common. At the heart of it, each represents a simple, hierarchical, contribution.</p>
<p>Episerver Social's <em></em><em>Comments</em> feature distills this concept down into a simple data structure that, in combination with the platform's data modeling concepts, can be applied to implement any such scenario.</p>
<h3><strong></strong></h3>
<h3><strong>What is a Comment?</strong></h3>
<p>The Episerver Social framework represents hierarchical content using a class called Comment (EPiServer.Social.Comments.Core.Comment). Fundamentally, a Comment is comprised of an <strong>Author</strong>, a <strong>Parent</strong>, and a <strong>Body</strong>. With these three things, we have enough information to build a conversation.</p>
<ul>
<li>The <strong>Body</strong> represents the content, which has been contributed.</li>
<li>The <strong>Author</strong> identifies the contributor of the content. Who posted the comment? Who contributed the review?</li>
<li>The <strong>Parent</strong> identifies what the content is in reference to. Is this a comment on a page in your site? Is this a reply to another comment?</li>
</ul>
<h3><strong></strong></h3>
<h3><strong>Relationships</strong></h3>
<p><span>What makes hierarchical content </span><em>hierarchical</em><span> are its relationships. When working with comments, there are two relationships to be aware of. The first is a comment's relationship to its <strong>Author</strong>. The second is a comment's relationship to its <strong>Parent</strong>. </span></p>
<p><span>Episerver Social uses an open system of references to allow an application to represent such relationships. This gives a developer the freedom to identify elements in their application (pages, users, products, etc.) in a manner that is appropriate for that application. The framework provides a class called Reference (EPiServer.Social.Common.Reference), which uses a custom string to identify an application element.</span></p>
<p>Let's look at how the concept applies to a Comment.</p>
<p><span style="text-decoration: underline;">Author</span></p>
<p>A Comment's <strong>Author</strong> is specified as a Reference, which identifies the contributing user. As the developer, you have the freedom to define the scheme used to identify that user.</p>
<p>So, in a simple web application, the username of an authenticated user may suffice for identifying a contributor.</p>
<pre class="language-csharp"><code>var contributor = Reference.Create(HttpContext.Current.User.Identity.Name);</code></pre>
<p><span style="font-size: 10pt;"><strong></strong></span>In scenarios where user identities come from many different providers, a more sophisticated scheme may be required. You might consider a custom URI scheme to identify contributors.</p>
<pre class="language-csharp"><code>var provider = ... // Set provider name
var username = ... // Set user name
var contributor = Reference.Create($"user://{provider}/{username}");</code></pre>
<p><span style="font-size: 10pt;"><strong></strong></span>These are just two examples. Others might include a user ID, a GUID, a serialized identity object. The key takeaway is that your contibutors can be identified in whatever manner suits your application. </p>
<p><span style="text-decoration: underline;">Parent</span></p>
<p>A Comment's <strong>Parent</strong> identifies what the comment is attached to. A comment can be attached to anything: a page, an Episerver Block, a product, an image, a user. Like the <strong>Author</strong>, a <strong>Parent</strong> is also identified using a Reference. So, you have the freedom to attach a Comment to anything that you can identify.</p>
<p>Let's say that we want to contribute a comment to an Episerver page. We might go about constructing our <strong>Parent</strong> Reference in the following manner.</p>
<pre class="language-csharp"><code>public Reference CreateReferenceFor(PageData page)
{
return Reference.Create(page.ContentGuid.ToString());
}</code></pre>
<p>Similary, if we wanted to contribute a comment to an Episerver Commerce product, our Reference might be constructed as shown below.</p>
<pre class="language-csharp"><code>public Reference CreateReferenceFor(ProductContent product)
{
return Reference.Create(product.Code);
}</code></pre>
<p>Now, while things like pages and products may be physically represented in a database somewhere, we're not strictly limited to such entities as a <strong>Parent</strong>. The ability to define our references means that we can also represent conceptual entities with no physical representation. Consider a feature where you'd like visitors to be able to discuss the facets of a product. A shirt's <em>fit</em> or a monitor's <em>clarity</em>. These are qualities of a product that likely have no stored entity to represent them but we can still have a conversation about them.</p>
<pre class="language-csharp"><code>var product = product.Code;
var facet = "fit";
var parent = Reference.Create($"product://{product}/{facet}");</code></pre>
<h3><strong></strong></h3>
<h3><strong>Contributing Comments</strong></h3>
<p>With an understanding of what comprises a Comment, we're ready to begin contributing them. Adding a Comment is a simple, two step, process:</p>
<ol>
<li>Construct the comment, defining its <strong>Body</strong>, <strong>Author, </strong>and <strong>Parent</strong>.</li>
<li>Submit the Comment to Episerver Social.</li>
</ol>
<p>All operations on Comments are performed through the ICommentService interface (EPiServer.Social.Comments.Core.ICommentService). A reference to <a href="/link/03f8ccf5bec34e048013d2824c8166ea.aspx#Accessing_ICommentService">this service can be obtained using Episerver's service locator</a>.</p>
<p>Let's continue the example of contributing comments to a page within Episerver. Using the concepts that we covered above, the sample below demonstrates how we might implement such a scenario.</p>
<pre class="language-csharp"><code>public class MyPageController : ContentController<MyContent>
{
private readonly ICommentService commentService;
public MyPageController()
{
commentService = EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance<ICommentService>();
}
// ...
private void SubmitComment(PageData page, string body)
{
// 1. Construct the comment, defining its Body, Author, and Parent
var comment = new Comment(
CreateReferenceFor(page),
CreateReferenceFor(HttpContext.Current.User.Identity),
body,
true
);
// 2. Submit the comment to Episerver Social.
commentService.Add(comment);
}
private Reference CreateReferenceFor(PageData page)
{
return Reference.Create(page.ContentGuid.ToString());
}
private Reference CreateReferenceFor(IIdentity user)
{
return user.IsAuthenticated
? Reference.Create(user.Name)
: Reference.Create("Anonymous");
}
}</code></pre>
<p>Now we can contribute comments to a page but conversation requires give-and-take. So, how do we reply to an existing comment? The process is very similar to what we've seen already with one small variation.</p>
<p>When a Comment is added to Episerver Social, ICommentService returns us a copy of that Comment populated with some system-generated information. The information includes a unique <strong>Id</strong> for the new Comment. When submitting a Comment as a reply to another, a Comment's <strong>Id</strong> is used as a <strong>Parent </strong>Reference.</p>
<p>Let's expand the controller class defined above with a new method for submitting a reply.</p>
<pre class="language-csharp"><code>private void SubmitReply(CommentId parentCommentId, string body)
{
// Convert the CommentId of the parent Comment into a Reference.
var parentCommentReference = parentCommentId.ToReference();
// Construct the comment, defining its Body, Author, and Parent
var comment = new Comment(
parentCommentReference,
CreateReferenceFor(HttpContext.Current.User.Identity),
body,
true
);
// Submit the comment to Episerver Social.
commentService.Add(comment);
}</code></pre>
<h3><strong></strong></h3>
<h3><strong>Retrieving Comments</strong></h3>
<p>With the ability to add and reply to Comments, our application likely also needs to deliver them. Episerver Social provides a number of ways to filter your Comments for retrieval. Your application may leverage any or all of those filtering capabilities as needed. Let's cover a couple of scenarios to introduce some of the options.</p>
<p>Let's first assume that our application simply maintains a linear list of Comments (i.e. no replies) for a page. To present such a list, our application would want to retrieve a set of Comments with a <strong>Parent</strong> corresponding to that page.</p>
<pre class="language-csharp"><code>private ResultPage<Comment> RetrieveComments(PageData page)
{
// Determine the parent for which Comments should be retrieved
var parent = CreateReferenceFor(page);
// Define the criteria by which Comments should be retrieved
var criteria = new Criteria<CommentFilter>()
{
Filter = new CommentFilter
{
Parent = parent
},
PageInfo = new PageInfo
{
PageSize = 10
}
};
// Retrieve the Comments from Episerver Social
return commentService.Get(criteria);
}</code></pre>
<p>In this example, we resolve the Reference representing the desired page and assign that Reference as a filter in the Criteria that is submitted to Episerver Social. As a result, Episerver Social delivers only Comments that are immediate children of the identified page. (The method also limits the result set to a maximum of 10 items.)</p>
<p>If we revise this method a bit, we can make it flexible enough to deliver Comments, regardless of whether the <strong>Parent</strong> is a page or another Comment.</p>
<pre class="language-csharp"><code>private ResultPage<Comment> RetrieveComments(Reference parent, int pageOffset = 0, int pageSize = 10)
{
// Define the criteria by which Comments should be retrieved
var criteria = new Criteria<CommentFilter>()
{
Filter = new CommentFilter
{
Parent = parent
},
PageInfo = new PageInfo
{
PageOffset = pageOffset,
PageSize = pageSize
}
};
// Retrieve the Comments from Episerver Social
return commentService.Get(criteria);
}</code></pre>
<p>Now we have the flexibility to retrieve comments that are children of any <strong>Parent</strong>. We can also dictate the size of our result set and the offset at which our result set should begin. </p>
<p>A best practice is to use such a method to reveal Comments to visitors on demand. Delivering a large volume of Comments can take time. To ensure the best experience, it is recommended that your application deliver smaller result sets (e.g. 10 items) and prompt the visitor to reveal more. Visitors often only read the first few Comments, so this approach helps to maximize performance without hindering the visitor's experience. Using the same method, the children of a Comment (its replies) can also be delivered.</p>
<p>The image below presents a mockup of how such an experience might prompt a visitor to show more Comments.</p>
<p><img src="/link/13db6974c03b4528871aa6f9e50fdff7.aspx" alt="Image comment-paging.PNG" /></p>
<p>In scenarios where only a handful of comments are expected, you can filter for an entire tree of Comments. To do so, we filter by a Comment's <strong>Ancestors</strong> rather than its <strong>Parent</strong>.</p>
<p>Episerver Social automatically tracks the lineage of References comprising each Comment's ancestry: a Comment's parent, grandparent, great-grandparent, and so on to the root. By filtering within that lineage, we can deliver an entire set of Comments which descend from it. So, to deliver the entirety of a Comment tree for a page we can filter for Comments where the page's Reference is present as an<strong> </strong>ancestor.</p>
<pre class="language-csharp"><code>private ResultPage<Comment> RetrieveAllComments(PageData page, int pageSize = 10)
{
// Define the criteria by which Comments should be retrieved
var criteria = new Criteria<CommentFilter>()
{
Filter = new CommentFilter
{
Ancestor = CreateReferenceFor(page)
},
PageInfo = new PageInfo
{
PageSize = pageSize
}
};
// Retrieve the Comments from Episerver Social
return commentService.Get(criteria);
}</code></pre>
<h3><strong>Going Deeper</strong></h3>
<p>With that, you have all that you need to get started with Comments in Episerver Social. There's plenty of more waiting for you when you're looking to dive deeper.</p>
<ul>
<li>The developer guide contains further explanation of <a href="/link/09c4c1e7f5154f2c94695075821e971b.aspx#Parents_ancestors">parents and ancestors</a> and everything else related to <a href="/link/03f8ccf5bec34e048013d2824c8166ea.aspx">working with Comments</a> (samples, best practices, and more).</li>
<li>A Gist with the source of the sample controller implementation used in this article can be found here: <a href="https://gist.github.com/cbanner/3d7fc4df4362ad0603208cc60425a53e">https://gist.github.com/cbanner/3d7fc4df4362ad0603208cc60425a53e</a>.</li>
<li>If you're interested in exploring how Comments may be further shaped and extended to include custom data, check out the post "<a href="/link/4ebdf45feff2418d8c910fe027b753e3.aspx">Building product reviews with Episerver Social</a>".</li>
</ul>
</body>
</html>What's new in Episerver Social 1.3.0?/blogs/chris-banner/dates/2017/4/whats-new-in-episerver-social-1-3-0/2017-04-24T06:00:00.0000000Z<p>This week brings the release of Episerver Social 1.3. I'd like to take a moment to highlight what's new in this release, and what you can look forward to from Episerver Social in the coming weeks and months.</p>
<h3><strong>What's new?</strong></h3>
<p>This release was largely focused on inner beauty: providing performance enhancements, internal reporting, and futher infrastructural support. In addition to these things, you'll also find a couple of new features in the client.</p>
<p><strong>Composites for Rating Statistics</strong></p>
<p>One of the Social R&D team's initiatives for this year is to further enhance the Ratings feature set within the platform. The first of those enhancements is available in 1.3.</p>
<p>As a system-calculated content structure, rating statistics lacked the ability to be extended with additional data. However, there are use cases where a developer may want to calculate statistics or store additional details of their own and persist them alongside those system-calculated statistics. For example, one might want to perform an out-of-band calculation for weighted averages and deliver them with their statistics.</p>
<p>In support of this, rating statistics may now be extended with custom data <a href="/link/a09c5c7c7ca54b9b8bcb06d846cf9031.aspx#composites">using the platform's Composite features</a>.</p>
<p><strong>Enhanced exceptions</strong></p>
<p>Episerver Social provides a suite of exceptions for communicating different types of errors that may occur in the course of interacting with the platform. We expanded upon that suite to provide further clarity and contextual details to aid you in any troubleshooting you may need to do. (Note: This expansion extends the existing hierarchy of exceptions, so as not to disrupt any exception handling that your application may already be performing.) Details on exception handling in Episerver Social can be found <a href="/link/a09c5c7c7ca54b9b8bcb06d846cf9031.aspx#exceptions">in the developer guide</a>.</p>
<h3><strong>What's coming?</strong></h3>
<p>The R&D team is actively working toward several 2017 initiatives. Highlights of an upcoming 1.4 release include:</p>
<ul>
<li>New filtering expressions, which include a logical OR and the ability to filter within collections</li>
<li>Mean value sorting for rating statistics</li>
<li>Performance enhancements to Composite filtering</li>
</ul>
<p>Beyond 1.4, there are some exciting changes to come:</p>
<ul>
<li>Extension data flexibility - A suite of changes is underway that focuses on giving you greater flexibility in how you customize, filter, and deliver your social content. This grants developers greater control over <em>what </em>content they wish to retrieve and also in determining <em>how </em>it should be delivered.</li>
<li>Effortless synchronicity - Relationships between an application and its Social content evolve over time. Applications need an easy way of communicating such changes. Another enhancement underway will allow developers to more easily construct and issue updates that transform broad sets of social content.</li>
<li>Educational materials - An ongoing goal for the Social team is enhancing the product's educational materials. In addition to the <a href="/link/9c1c5585ade74ebba82aa88fae6c1c4c.aspx">developer guide</a> and the introduction of <a href="/link/2670afc9db0b4449a862e69579780ae1.aspx">a dedicated forum</a>, the R&D team is <a href="/link/dbf6c51231e14ebcba48adf67803fa0e.aspx">contributing to a Social team blog</a>. We also look forward to introducing a portal for provisioning demonstration accounts, allowing developers to explore the platform in a sandbox.</li>
</ul>Building product reviews with Episerver Social/blogs/chris-banner/dates/2017/3/building-product-reviews-with-episerver-social/2017-03-24T11:00:00.0000000Z<p>Product reviews are a very common form of user-generated content (UGC) in commerce sites. If I were to ask you to describe to me what a “product review” is, however, I suspect that it would differ significantly from what anyone else might envision.</p>
<ul>
<li>It's simple content -- maybe threaded, maybe linear.</li>
<li>Perhaps there are one or more ratings to express the quality of various facets of the product.</li>
<li>Maybe there are additional business details, such as a purchase date, to be captured.</li>
</ul>
<p>The fact is that every organization has a different perspective on what comprises a product review.</p>
<p>As a UGC platform, Episerver Social is tailored toward implementing these types of solutions. The developer has creative control to model their UGC to suit their particular use cases. They can leverage the platform's native social constructs, then can compose them and customize them to their needs, and do so in a system tailored to supporting the demands of delivering UGC.</p>
<p>The Episerver Social team has recently released a demo <a href="https://github.com/episerver/SocialQuicksilver">implementation of product reviews on GitHub</a>. This has been built in a clone of Episerver's Quicksilver commerce demonstration and is intended to illustrate:</p>
<ul>
<li>Modeling user-generated content</li>
<li>Defining relationships between UGC and site resources</li>
<li>Use of the Episerver Social framework to manage and deliver content</li>
</ul>
<p>I encourage you to download it, configure it with your Episerver Social account, and play with the reviews found on its product pages.</p>
<h3><strong>Composing reviews</strong></h3>
<p>Episerver Social provides fundamental building blocks for modeling social content. These building blocks can be applied in any number of ways and may be further enhanced with data of your own design. Before setting out to implement your social features, it’s important to consider how you intend your social content to be composed.</p>
<p>Consider the mockup of product reviews below to be a visual representation of our requirements for this implementation.</p>
<p><img alt="Image mockup.png" src="/link/bb8f07c85bf841ea8452f3efecb6b898.aspx" /></p>
<p>When considering this content, we can see several things:</p>
<ul>
<li>The focal point of an individual review appears to be the contributor's message.</li>
<li>A review displays a contributor's:</li>
<ul>
<li>Nickname</li>
<li>Location</li>
<li>Date of contribution</li>
<li>Review title</li>
<li>Review body</li>
</ul>
<li>The reviews are linear.</li>
<li>The most recent N reviews are being displayed.</li>
<li>Each review appears to have an accompanying rating.</li>
<li>An average rating appears atop the list of reviews.</li>
</ul>
<p>These considerations factor into how we ultimately model our social content. Each Episerver Social feature can be thought of more generally as basic social data structures. A comment is hierarchical content, a rating is appraised content, moderation is stateful content, and so on.</p>
<p>With that in mind, how might we apply Episerver Social to model the reviews detailed in the mockup above?</p>
<ul>
<li>If the message is the focal point, a comment may be suitable as the core data structure from which to shape our content.</li>
<ul>
<li>The native model already accommodates the notion of a content body and contribution date.</li>
<li>Its hierarchical nature may suit us well if reviews are intended to allow for replies (now or in the future).</li>
<li>It can be easily queried to deliver the most recent <em>N</em> contributions.</li>
</ul>
<li>Ratings provide an efficient means of accumulating values and tabulating averages.</li>
<li>By allowing us to define and store a custom data model alongside the native data structures, Episerver Social’s Composites feature provides a convenient extensibility point for capturing the review's remaining attributes.</li>
</ul>
<p>The diagram below demonstrates how Episerver Social features might be composed with extended data to design a product review. In subsequent activities, we will implement this model.</p>
<p><img alt="Image model.png" src="/link/0e632f9d6d5c49488ea8611fa23fbd1d.aspx" /></p>
<h3> </h3>
<h3><strong>Referencing content and users</strong></h3>
<p>Social content is largely supplementary in the sense that it compliments some other application entity. For example, a comment is contributed to an article, an image, etc. A comment is also contributed by some user or site visitor.</p>
<p>These users and content items may take many different forms and may reside in many different repositories. For that reason, Episerver Social maintains these relationships by reference.</p>
<p>A <a href="/link/a09c5c7c7ca54b9b8bcb06d846cf9031.aspx#referenceandIDs">Reference</a> establishes a relationship between social content and the resources or users to which it applies. It enables you to associate social content with any uniquely identifiable entity. Episerver Social does not need to know anything about what your application entities are or where they reside.</p>
<p>So, we need to define a reference scheme for a couple of different application entities:</p>
<ul>
<li>The product being reviewed</li>
<ul>
<li>An Episerver Social Comment must be associated with some parent entity (the thing being commented upon).</li>
<li>An Episerver Social Rating must be attributed to some target entity (the thing being rated).</li>
</ul>
<li>The contributor of the review</li>
<ul>
<li>An Episerver Social Comment may optionally be assigned an author.</li>
<li>An Episerver Social Rating must identify the rater.</li>
</ul>
</ul>
<p>A good Reference is one that is capable of uniquely identifying your application’s entities and should be easily interpreted by your application. A custom URI often provides a suitable scheme.</p>
<p>Take a look at the <a href="https://github.com/episerver/SocialQuicksilver/blob/master/Sources/EPiServer.Reference.Social.Reviews/Services/ReviewService.cs">ReviewService</a>, found in the demo implementation of product reviews. This service encapsulates the business logic supporting our product review implementation.</p>
<p>Within the <strong>Add(ReviewSumissionViewModel)</strong> method, which is responsible for saving new reviews, we must first identify the product being reviewed and the contributor of the review. To do so, we must construct a <strong>Reference</strong>.</p>
<pre class="language-csharp"><code>/// <summary>
/// Creates a reference identifying a review contributor.
/// </summary>
/// <param name="nickname">Nickname identifying the review contributor</param>
/// <returns>Reference identifying a review contributor</returns>
private static EPiServer.Social.Common.Reference CreateContributorReference(string nickname)
{
return EPiServer.Social.Common.Reference.Create($"visitor://{nickname}");
}
/// <summary>
/// Creates a reference identifying a product.
/// </summary>
/// <param name="nickname">Content code identifying the product</param>
/// <returns>Reference identifying a product</returns>
private static EPiServer.Social.Common.Reference CreateProductReference(string productCode)
{
return EPiServer.Social.Common.Reference.Create($"product://{productCode}");
}</code></pre>
<p>The supporting CreateContributorReference(string) and CreateProductReference(string) methods in this class demonstrate the construction of these references. You'll notice, in both cases, that we are using a simple URI format for a scheme.</p>
<p>For the contributor, we are leveraging the contributor's nickname as their identifier. Admittedly, this is not a good <em>unique</em> identifier. You might consider a visitor's user ID, username, and the type of membership provider in defining your reference scheme.</p>
<p>For the product, we are leveraging its product code as an identifier. Depending on your requirements, a URI might also account for variant identifiers or product facets such as fit, size, and feel.</p>
<h3><strong>Adding a rating</strong></h3>
<p>We have just defined how to represent relationships between our application and our social content. Now, we can begin to store that social content. First, we’ll add the rating that is submitted with a review.</p>
<p>Ratings are described with a simple model, called <strong>Rating</strong>, and are managed through the Episerver Social framework’s <strong>IRatingService</strong> interface.</p>
<p>To construct a rating, we must provide:</p>
<ul>
<li>A target (Reference) - the thing being rated</li>
<li>A rater (Reference) - the contributor of the rating</li>
<li>A value (RatingValue) - the rating's numeric value</li>
</ul>
<p>We previously defined References for the product being reviewed (the target) and the contributor of the review (the rater). We will use those references now, as we construct and store a rating.</p>
<p>In the ReviewService's Add(ReviewSubmissionViewModel) method, you'll notice the following:</p>
<pre class="language-csharp"><code>// Add the contributor's rating for the product
var submittedRating = new Rating(contributor, product, new RatingValue(review.Rating));
var storedRating = this.ratingService.Add(submittedRating);</code></pre>
<p>With this code, we constructed a <strong>Rating</strong> and submitted it with the <strong>Add(Rating)</strong> method of the <strong>IRatingService</strong>. The method returns a new instance of Rating, which has been populated with additional system-generated (e.g. an ID, timestamp, etc.). We will use some of that data shortly.</p>
<p>As each Rating is added to the system, Episerver Social is automatically tabulating the statistics for the target of that rating (our product). We will retrieve those statistics when we present the reviews later.</p>
<p><em>Note: The Ratings feature allows one rating per contributor (rater) for a target. If you encounter a <strong>DuplicateRatingException</strong> while submitting a review, you are using the nickname of a visitor who has already contributed a review for the product. Choose an alternative nickname.</em></p>
<h3><strong>Modeling a composite comment</strong></h3>
<p>Our application is now capable of storing the rating that accompanies a review, but we have yet to deal with the "meat" of the review.</p>
<p>Episerver Social’s Comment gives us a convenient starting place. It accommodates a message body, an author, a contribution date, and a relationship to application content (e.g. the products). However, our requirements dictate that we must display additional information. This includes a review title as well as the contributor’s location and nickname.</p>
<p>Episerver Social allows you to extend any of its core data models by pairing them with data of your own design. This pairing is called a <strong>Composite</strong>. The data that you design for a Composite is referred to as extension data. Extension data is simply a serializable .NET class defined within your application.</p>
<p>The Composites that you define can be stored and retrieved just like native Episerver Social models. The values contained with your extension data can even be applied in custom filtering and sorting.</p>
<p>Examine the <a href="https://github.com/episerver/SocialQuicksilver/blob/master/Sources/EPiServer.Reference.Social.Reviews/Composites/Review.cs">Review</a> and <a href="https://github.com/episerver/SocialQuicksilver/blob/master/Sources/EPiServer.Reference.Social.Reviews/Composites/ReviewRating.cs">ReviewRating</a> classes, defined in our demo implementation.</p>
<pre class="language-csharp"><code>public class Review
{
public string Title { get; set; }
public string Nickname { get; set; }
public string Location { get; set; }
public ReviewRating Rating { get; set; }
}</code></pre>
<pre class="language-csharp"><code>public class ReviewRating
{
public int Value { get; set; }
public string Reference { get; set; }
}</code></pre>
<p>Compare the implementation of these extension data classes and the definition of the <a href="/link/09c4c1e7f5154f2c94695075821e971b.aspx">Episerver Social <strong>Comment</strong></a> class with the logical model of a product review (illustrated above in "Composing reviews"). Notice how the Comment and extension data complement each other to fulfil the logical model that we brainstormed.</p>
<h3><strong>Adding a composite comment</strong></h3>
<p>With our extension data defined, we’re finally ready to store the review. We will store this as a <strong>Composite</strong> of <strong>Comment</strong> and <strong>Review </strong>(our extension data class). Comments are managed through the Episerver Social framework’s <strong>ICommentService</strong> interface.</p>
<p>To construct a Comment, we must provide:</p>
<ul>
<li>A parent (Reference) – the thing being commented upon</li>
<li>An author (Reference) – the comment contributor</li>
<li>A body (String) – the content of the comment</li>
<li>A visibility flag (Boolean) – the initial status of the comment</li>
</ul>
<p>We have previously defined References for the product being reviewed (the parent) and the contributor of the review (the author). We will use those references again to construct our Comment. We will use our Review extension data class to supply the remaining details.</p>
<p>Again, in the <a href="https://github.com/episerver/SocialQuicksilver/blob/master/Sources/EPiServer.Reference.Social.Reviews/Services/ReviewService.cs">ReviewService</a>'s Add(ReviewSubmissionViewModel) method, you'll notice the following:</p>
<pre class="language-csharp"><code>// Compose a comment representing the review
var comment = new Comment(product, contributor, review.Body, true);
var extension = new Review
{
Title = review.Title,
Location = review.Location,
Nickname = review.Nickname,
Rating = new ReviewRating
{
Value = review.Rating,
Reference = storedRating.Id.Id
}
};
// Add the composite comment for the product
this.commentService.Add(comment, extension);</code></pre>
<p>Here, we instantiated both a <strong>Comment</strong> and our <strong>Review</strong> extension data. We have populated with the inputs from the review submission, and stored it using Episerver Social's <strong>ICommentService</strong>.</p>
<h3><strong>Retrieving rating statistics</strong></h3>
<p>In addition to managing ratings, the <strong>IRatingService</strong> also allows you to retrieve the aggregate statistics for a target (the thing being rated). These statistics are automatically tabulated by Episerver Social as ratings accumulate in the system.</p>
<p>These statistics allow us to calculate the overall rating for a product, as shown highlighted in blue in the mockup below.</p>
<p><img alt="Image statistics.png" src="/link/90ca67871aeb4e4fa4ce1546977d98b6.aspx" /></p>
<p>To retrieve the statistics for a product, we will use the <strong>Get(Criteria<RatingStatisticsFilter>)</strong> method of IRatingService. This method accepts criteria, which allows us to specify the parameters of our query, and returns a page of <strong>RatingStatistics</strong>. Those parameters include filters, sorting rules, and paging information.</p>
<p>The <a href="https://github.com/episerver/SocialQuicksilver/blob/master/Sources/EPiServer.Reference.Social.Reviews/Services/ReviewService.cs">ReviewService</a>'s GetProductStatistics(Reference) method implements the logic necessary to retrieve the tabulated rating statistics for the product:</p>
<pre class="language-csharp"><code>/// <summary>
/// Gets the rating statistics for the identified product
/// </summary>
/// <param name="product">Reference identifying the product</param>
/// <returns>Rating statistics for the product</returns>
private RatingStatistics GetProductStatistics(EPiServer.Social.Common.Reference product)
{
var statisticsCriteria = new Criteria<RatingStatisticsFilter>()
{
Filter = new RatingStatisticsFilter()
{
Targets = new[] { product }
},
PageInfo = new PageInfo()
{
PageSize = 1
}
};
return this.ratingService.Get(statisticsCriteria).Results.FirstOrDefault();
}</code></pre>
<p>The method accepts a Reference identifying the product, for which statistics should be retrieved, and uses it to define a query <strong>Criteria</strong> identifying it as the target. The <strong>Criteria</strong> also includes paging information dictating that a single result should be returned. Finally, it passes the criteria to the <strong>Get(Criteria<RatingStatisticsFilter>) </strong>method of <strong>IRatingService</strong> to retrieve the product's statistics.</p>
<h3><strong>Retrieving composite comments</strong></h3>
<p>All that is remaining in our implementation now is the retrieval of the <strong>Composite Comments,</strong> which represent our reviews. Much like <strong>IRatingService</strong>, <strong>ICommentService</strong> also allows you to specify criteria to retrieve the content that it manages.</p>
<p>For retrieving <strong>Composites</strong>, all Episerver Social services provide an overloaded method <strong>Get(CompositeCriteria<TFilter, TExtension>)</strong>. This criteria provides you access to all of the native filtering and sorting capabilities while adding the ability to define custom filters against your extension data fields.</p>
<p>We will use <strong>CompositeCriteria<CommentFilter,Review></strong> to retrieve the <strong>Composite Comments</strong> for our application.</p>
<p>The <a href="https://github.com/episerver/SocialQuicksilver/blob/master/Sources/EPiServer.Reference.Social.Reviews/Services/ReviewService.cs">ReviewService</a>'s GetProductReviews(Reference) method implements the logic necessary to retrieve the comments that have been contributed for the product:</p>
<pre class="language-csharp"><code>/// <summary>
/// Gets a collection of reviews for the identified product.
/// </summary>
/// <param name="product">Reference identifying the product</param>
/// <returns>Collection of reviews for the product</returns>
private IEnumerable<Composite<Comment, Review>> GetProductReviews(EPiServer.Social.Common.Reference product)
{
var commentCriteria = new CompositeCriteria<CommentFilter, Review>()
{
Filter = new CommentFilter
{
Parent = product
},
PageInfo = new PageInfo
{
PageSize = 20
},
OrderBy = new List<SortInfo>
{
new SortInfo(CommentSortFields.Created, false)
}
};
return this.commentService.Get(commentCriteria).Results;
}</code></pre>
<p>This method defines criteria that will retrieve the most recent twenty <strong>Composite Comments</strong> for the specified product. The criteria includes a parent filter, identifying the product. It includes paging information, dictating that up to 20 results should be returned. It also includes a sorting rule, indicating that results should be ordered by creation date, in descending order.</p>
<h3><strong>Presenting reviews</strong></h3>
<p>Finally, our application must adapt the comments and statistical data that have been retrieved into a form appropriate for presentation in the view. This demonstration <a href="https://github.com/episerver/SocialQuicksilver/blob/master/Sources/EPiServer.Reference.Social.Reviews/Services/ViewModelAdapter.cs">implements a simple adapter pattern</a> to perform this translation.</p>
<pre class="language-csharp"><code>internal static class ViewModelAdapter
{
public static ReviewStatisticsViewModel Adapt(RatingStatistics statistics)
{
var viewModel = new ReviewStatisticsViewModel();
if (statistics != null)
{
viewModel.OverallRating = Convert.ToDouble(statistics.Sum) / Convert.ToDouble(statistics.TotalCount);
viewModel.TotalRatings = statistics.TotalCount;
}
return viewModel;
}
public static IEnumerable<ReviewViewModel> Adapt(IEnumerable<Composite<Comment, Review>> reviews)
{
return reviews.Select(Adapt);
}
private static ReviewViewModel Adapt(Composite<Comment, Review> review)
{
return new ReviewViewModel
{
AddedOn = review.Data.Created,
Body = review.Data.Body,
Location = review.Extension.Location,
Nickname = review.Extension.Nickname,
Rating = review.Extension.Rating.Value,
Title = review.Extension.Title
};
}
}</code></pre>
<p>Voilà! You've implemented a product review with Episerver Social.</p>What's new in Episerver Social 1.2.0?/blogs/chris-banner/dates/2017/1/whats-new-in-episerver-social-1-2-0/2017-01-30T00:51:58.1930000Z<p>The R&D team is excited to announce that Episerver Social 1.2.0 releases this week. It includes a number of new features for developers. Let's take a look at what's in it:</p>
<h3><strong>Asynchronous overloads for all services</strong></h3>
<p>All methods of the Episerver Social framework's services now have <a href="/link/a09c5c7c7ca54b9b8bcb06d846cf9031.aspx#async_methods">asynchronous overloads</a> allowing developers to take advantage of .NET's async/await capabilities and allows the framework to accomodate a broader range of development paradigms. The developer guide has been updated with samples demonstrating the use of each of the new overloads.</p>
<h3><strong>EPiServer.Social.Framework NuGet package</strong></h3>
<p>For developers looking to take advantage of Episerver Social's full suite of features, we've added the EPiServer.Social.Framework NuGet package. This is a light package which will install all of the Episerver Social features: Comments, Ratings, Moderation, Activity Streams, and Groups.</p>
<h3><strong>Update method for Roles</strong></h3>
<p>In a minor enhancement to the RoleService, an Update method has added allowing you to update composite Roles. (This also includes an asynchronous overload.)</p>
<h3><strong>Social Alloy demonstration application</strong></h3>
<p>A demonstration application for Episerver Social features is <a href="https://github.com/episerver/SocialAlloy">now available on GitHub</a>. This application is a clone Episerver's Alloy demonstration site, enhanced with Episerver Social capabilities. In its initial revision, this site includes a series of Episerver blocks which can be added to pages to explore the feature set. This will continue to be enhanced with additional demonstrations and developer samples, so make sure to bookmark the repo.</p>
<p>In the coming weeks, the R&D team will be focused on a number of initiatives which include:</p>
<ul>
<li>Additional tutorials and demonstrations</li>
<li>Enhancements to boost the capabilities of the Ratings feature</li>
<li>Further internal service improvements</li>
</ul>
<p>And finally, be sure to <a href="http://www.episerver.com/about-us/ascend-usa/">join me at Episerver Ascend USA 2016</a> for a hands-on lab in which we'll introduce the platform, discuss how its features can be composed to create product reviews, and then implement them in a commerce site. See you there!</p>