<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by Benjamin Schaefer</title><link href="http://world.optimizely.com" /><updated>2017-09-12T19:15:28.0000000Z</updated><id>https://world.optimizely.com/blogs/benjamin-schaefer/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>Getting started with Activity Streams: Part 1</title><link href="https://world.optimizely.com/blogs/benjamin-schaefer/dates/2017/9/getting-started-with-activity-streams-using-social-alloy/" /><id>&lt;p&gt;&lt;strong&gt;Overview&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the first post in a series that will provide deeper insight into the Episerver Social Activity Streams&amp;nbsp;functionality. This post targets how Activity Streams has been implemented within Social Alloy. Upcoming posts will look specifically at &lt;span&gt;best practices&amp;nbsp;for working with&amp;nbsp;&lt;/span&gt;the Activity Streams APIs and alternative use cases for where Activity Streams can add business value.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The engagement of digital community members can take many forms. Reviewing a product, joining a group, adding a comment or friending another member, among many others. While each activity is unique, the ability to broadcast, and promote your community&#39;s engagement can be a powerful tool for any digital marketer.&lt;/p&gt;
&lt;p&gt;Episerver Social Activity Streams provide the functionality to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Easily publish activities related to your user’s actions.&lt;/li&gt;
&lt;li&gt;Allow users to engage with these published activities through subscriptions.&lt;/li&gt;
&lt;li&gt;Return related information for activities that were published to subscribed users.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Conveniently, the Episerver Social Alloy demo site has an implementation of a typical use case for Activity Streams. This post will look at how the CommentBlock, SubscriptionBlock and FeedBlock in Social Alloy tie into Activity Streams on the pre-populated Silver Resellers Group page. To follow along, I encourage you to investigate the Social Alloy GitHub repo found here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/episerver/SocialAlloy&quot;&gt;https://github.com/episerver/SocialAlloy&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And to experiment with this functionality take advantage of the demo account portal for Episerver Social. Sign up here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://social.episerver.net&quot;&gt;https://social.episerver.net&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Background&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before we jump into the implementation of Activity Streams it is useful to understand some of key terms that relate to this feature.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;Activities&lt;/span&gt;:&amp;nbsp;Activities signify an event or action occurring within your application. The Episerver Social platform allows for activities to be published within an application, resulting in the generation of corresponding feed items for subscribing users.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;Subscriptions&lt;/span&gt;: Users may subscribe to resources or other users within your application. When that occurs, the system generates a record of activities related to those resources and users. That information can subsequently be filtered and retrieved in the form of a feed.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;Feeds&lt;/span&gt;: A feed represents a record of activities occurring within the application. As an activity is added to the system, the platform generates feed items for users subscribed to the entity upon which the activity occurred.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;Actor&lt;/span&gt;: A reference for the user or system that triggers an activity&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;Target&lt;/span&gt;: A reference for what was acted upon. The target can take many forms such as a page, a system event, or user action.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;With these terms in mind, we can begin to see the relationship between all of the components needed to implement a robust streaming activity system.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The CommentBlock in Social Alloy can be configured for Activity Stream functionality.&amp;nbsp;The CommentBlock can create an activity for each&amp;nbsp;comment that is added&amp;nbsp;to a&amp;nbsp;page&amp;nbsp;where the block resides. The block configuration code is here:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Configures whether an activity should be sent to the Episerver Social Activity Streams system.
/// &amp;lt;/summary&amp;gt;
[Display(
	GroupName = SystemTabNames.Content,
	Order = 1)]
public virtual bool SendActivity { get; set; }

/// &amp;lt;summary&amp;gt;
/// Sets the default configuration values.
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name=&quot;contentType&quot;&amp;gt;Type of the content.&amp;lt;/param&amp;gt;
public override void SetDefaultValues(ContentType contentType)
{
	base.SetDefaultValues(contentType);
	ShowHeading = false;
	Heading = &quot;Comments&quot;;
	CommentBoxRows = 5;
	CommentMaxLength = 500;
	CommentsDisplayMax = 10;
	SendActivity = true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The CommentBlockController contains the logic for adding an activity to Social when a new comment has been added.&amp;nbsp;SocialAlloy abstracts its interaction with Episerver Social into repository implementations. These repositories have knowledge of the application’s business logic and models, which they manage through Episerver Social. Adding an activity requires an actor, a target ,and extension data. For this implementation, the actor will be the user id of the comment author, the target will be the page id where the CommentBlock resides, and the extension data will be the additional relevant information for the event of a comment being added to the system. The only additional information needed in this scenario is the comment body. The activity creation code is here&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;private void AddCommentActivity(PageComment comment)
{
	try
	{
		var commentActivity = new PageCommentActivity { Body = comment.Body };

		this.activityRepository.Add(comment.AuthorId, comment.Target, commentActivity);
	}
	catch (SocialRepositoryException ex)
	{
		AddMessage(MessageKey, new MessageViewModel(ex.Message, ErrorMessage));
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An example of a CommentBlock directly tied to activity creation can be seen on the Silver Reseller Group page. Additionally,&amp;nbsp;this page has a Subscription Block that contains functionality for a user to subscribe or unsubscribe to activities that occur on this page.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Silver Resellers Group&quot; src=&quot;/link/174e959f2b294225a6d2ece5e6487571.aspx&quot; height=&quot;1027&quot; width=&quot;1290&quot; /&gt;&lt;/p&gt;
&lt;p&gt;By clicking subscribe on the SubscriptionBlock, users can indicate that they want to be informed of all activities that occur on the SilverResellers Group page.&amp;nbsp; A user may have multiple subscriptions to different targets. In Social Alloy, there are three pre-configured Resellers Group pages with activity creation that users can subscribe to. The handling of adding or removing a subscription for a user on a page can be found in the SubscriptionBlockController.cs in the HandleAction method:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Handle subscribe/unsubscribe actions.
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name=&quot;actionName&quot;&amp;gt;The action.&amp;lt;/param&amp;gt;
/// &amp;lt;param name=&quot;formViewModel&quot;&amp;gt;The form view model.&amp;lt;/param&amp;gt;
/// &amp;lt;returns&amp;gt;The action result.&amp;lt;/returns&amp;gt;
private ActionResult HandleAction(string actionName, SubscriptionFormViewModel formViewModel)
{
var subscription = new PageSubscription
{
	Subscriber = this.userRepository.GetUserId(this.User),
	Target = this.pageRepository.GetPageId(formViewModel.CurrentPageLink),
}; 

try
{
	if (actionName == Action_Subscribe)
	{
		this.subscriptionRepository.Add(subscription);
	}
	else
	{
		this.subscriptionRepository.Remove(subscription);
	}
	AddMessage(MessageKey, new MessageViewModel(SubmitSuccessMessage, SuccessMessage));
}
catch (SocialRepositoryException ex)
{
	AddMessage(MessageKey, new MessageViewModel(ex.Message, ErrorMessage));
}

return Redirect(UrlResolver.Current.GetUrl(formViewModel.CurrentPageLink));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p class=&quot;informationBox&quot;&gt;Note: The activity targets the page that the SubscriptionBlock is on. This is the same target as the activity that is generated by the CommentBlock when it has been configured for activity generation on comment submission. This relationship between activity target and subscription target helps to explain the creation of feed items for a subscribed user. If an activity has the same target as the target that a user is subscribed to, a feed item will be generated.&lt;/p&gt;
&lt;p&gt;When a user has subscribed to the Silver Reseller Group page, a feed item will be generated for them every time a contributor on this page adds a comment. To see these feed items, a user can use the top level menu to navigate to their Profile page. This page will provide the user with a feed of information relating to the activities they have subscribed to. To display feed items, the Profile page uses the FeedBlock. Below shows the FeedBlock code for retrieving the feed items associated with the logged in user.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Gets the activity feed for the logged in user
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name=&quot;currentBlock&quot;&amp;gt;The current frontend block instance.&amp;lt;/param&amp;gt;
/// &amp;lt;param name=&quot;blockViewModel&quot;&amp;gt;a reference to the FeedBlockViewModel to 
///populate with activity feed for the logged in user and errors, if any&amp;lt;/param&amp;gt;
private void GetSocialActivityFeed(FeedBlock currentBlock, FeedBlockViewModel blockViewModel)
{
	try
	{
		var userId = userRepository.GetUserId(this.User);
		if (!String.IsNullOrWhiteSpace(userId))
		{
			blockViewModel.Feed =
				this.feedRepository.Get(new CommunityFeedFilter
				{
					Subscriber = userId,
					PageSize = currentBlock.FeedDisplayMax
				});
		}
		else
		{
			blockViewModel.Messages.Add(new MessageViewModel(ErrorGettingUserIdMessage, ErrorMessage));
		}
	}
	catch (SocialRepositoryException ex)
	{
		blockViewModel.Messages.Add(new MessageViewModel(ex.Message, ErrorMessage));
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As more comments are added to the&amp;nbsp;page(s) that a user is subscribed to, more feed items will be retrieved by the Feed API and displayed in the FeedBlock. Within the Social Alloy demo, other blocks can also be configured to generate activities. If a user has subscribed to a page with those blocks in Social Alloy, the&amp;nbsp;FeedBlock&amp;nbsp;will display those related feed items as well.&lt;br /&gt;&lt;img alt=&quot;Profile page highlighted&quot; src=&quot;/link/77f5a7fbc51b47ee8a33d32f23e081f3.aspx&quot; height=&quot;892&quot; width=&quot;1280&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;informationBox&quot;&gt;Note: A&amp;nbsp;feed item&amp;nbsp;will encapsulate the extension data of the activity that was originally added to the system. This allows developers tremendous flexibility for the information that can be conveyed within a user&#39;s feed. In the example above, an activity was added which contained the comment body&amp;nbsp;as its extension data. The FeedBlock displays that content as well as the comment author (actor) and page where that comment was added (target). This information&amp;nbsp;originated from the comment that was added using the CommentBlock on the SilverResellers Group page.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This implementation of activity creation, user subscription, and feed generation is &lt;span&gt;rudimentary&amp;nbsp;&lt;/span&gt;but should help to illustrate the power of the Activity Streams functionality within Episerver Social for building connected online communities.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;For more information regarding Activity Streams, please take a look at the developer documentation here:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/link/8541347b3a51455a82f0b5466841e7f1.aspx&quot;&gt;http://world.episerver.com/documentation/developer-guides/social/activity-streams-introduction/&lt;/a&gt;&lt;/p&gt;</id><updated>2017-09-12T19:15:28.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Working with Episerver Social References</title><link href="https://world.optimizely.com/blogs/benjamin-schaefer/dates/2017/4/Workin-with-Episerver-Social-References/" /><id>&lt;p&gt;Episerver Social is a stand-alone platform that provides unparalleled control over the construction of models used to deliver social content. Social has two ways to uniquely reference content stored within it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An Id that is generated when a model is persisted into the Social system.&lt;/li&gt;
&lt;li&gt;A Reference, a means of identifying relationships between social content and application entities.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One aspect of building a maintainable social application is ensuring that you apply a consistent scheme for representing the relationships between your application and its social content. References must be capable of uniquely identifying your application&#39;s entities, being easily interpreted by your application, and robust enough to support your application&#39;s inevitable future changes.&lt;/p&gt;
&lt;p&gt;Example use cases for References might include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Referencing a third-party platform that Social is interacting with (for example, Episerver, Ektron, SAP, etc.)&lt;/li&gt;
&lt;li&gt;Referencing a third-party provider that Social is being used with (for example, Commerce, Cms, ERP, etc.)&lt;/li&gt;
&lt;li&gt;Referencing unique user Ids&lt;/li&gt;
&lt;li&gt;Referencing unique product/variant Ids&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To ensure the consistent construction of a reference a developer might find it advantageous to standardize the way references are created. Here are three guidelines to help you to create references.&lt;/p&gt;
&lt;p&gt;1. Build an easily expandable mechanism for handling strings that will be used most often.&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;//Outline the standard platforms that will be in use with Social.
public static class Platform
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string Episerver = &quot;Episerver&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string Ektron = &quot;Ektron&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string Salesforce = &quot;Salesforce&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string SAP = &quot;SAP&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string Microsoft = &quot;Microsoft&quot;;
}
//Outline the standard providers that exist for your platforms and will be referenced by your builder. 
public static class Provider
{ 
&amp;nbsp;&amp;nbsp;&amp;nbsp; //Episever and Ektron
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string CMS = &quot;CMS&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string Commerce = &quot;Commerce&quot;;
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; //Salesforce
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string Demandware = &quot;Demandware&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string SalesCloud = &quot;SalesCloud&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string Pardot = &quot;Pardot&quot;;
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; //SAP
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string ERP = &quot;ERP&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string Anywhere = &quot;Anywhere&quot;;
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp; //Microsoft
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string Sharepoint = &quot;Sharepoint&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp; public const string Dynamics = &quot;Dynamics&quot;;
}&amp;nbsp;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;2. Build an efficient way to assemble the relevant strings needed for your Social References.&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;// This class builds up a string from the relevant details of a product in a third party system.
public class ProductReferenceBuilder
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; public string Platform { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; public string Provider { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; public string ProductId { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; public string Variant { get; set; }

&amp;nbsp;   public ProductReferenceBuilder AddPlatform(string platform)
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Platform = platform;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return this;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;   &lt;br /&gt;    public ProductReferenceBuilder AddProvider(string provider)
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Provider = provider;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return this;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;br /&gt;    public ProductReferenceBuilder AddProductId(string productId)
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProductId = productId;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return this;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;br /&gt;    public ProductReferenceBuilder AddVariantId(string variant)
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Variant = variant;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return this;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
 &lt;br /&gt;  &amp;nbsp; public Reference Build()
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProductReferenceValidation();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var referenceString = $&quot;Product://{Platform}/{Provider}/{ProductId}/{Variant}&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return Reference.Create(referenceString);
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;br /&gt;    private void ProductReferenceValidation()
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (string.IsNullOrWhiteSpace(Platform) ||
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string.IsNullOrWhiteSpace(Provider) ||
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string.IsNullOrWhiteSpace(ProductId) ||
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string.IsNullOrWhiteSpace(Variant))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw new Exception(&quot;Missing field value for ProductReference&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;br /&gt;// This class builds up a string from the relevant details of a user in a third-party system.
public class UserReferenceBuilder
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; public string Platform { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; public string Provider { get; set; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; public string UserId { get; set; }
&lt;br /&gt;    public UserReferenceBuilder AddPlatform(string platform)
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Platform = platform;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return this;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;br /&gt;  &amp;nbsp; public UserReferenceBuilder AddProvider(string provider)
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Provider = provider;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return this;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;br /&gt;    public UserReferenceBuilder AddUserId(string userId)
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; UserId = userId;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return this;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
 &lt;br /&gt;    public Reference Build()
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProductReferenceValidation();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var referenceString = $&quot;User://{Platform}/{Provider}/{UserId}&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return Reference.Create(referenceString);
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;br /&gt;    private void ProductReferenceValidation()
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (string.IsNullOrWhiteSpace(Platform) ||
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string.IsNullOrWhiteSpace(Provider) ||
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string.IsNullOrWhiteSpace(UserId))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw new Exception(&quot;Missing field value for UserReference&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&lt;br /&gt;    private void ProductReferenceValidation()
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (string.IsNullOrWhiteSpace(Platform) ||
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string.IsNullOrWhiteSpace(Provider) ||
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string.IsNullOrWhiteSpace(UserId))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw new Exception(&quot;Missing field value for UserReference&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;3. Add unit tests for your reference related classes to ensure that the creation of a reference matches what is expected.&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[TestClass]
public class ProductBuilderTest
{
&amp;nbsp;&amp;nbsp;&amp;nbsp; [TestMethod]
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void ProductReferenceBuilder_Build_ReturnsProperlyConstructedReference()
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
         var expectedProductReference = Reference.Create(&quot;Product://Episerver/Commerce/ProductId/VarientId&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;var actualProductReference = new ProductReferenceBuilder().AddPlatform(Platform.Episerver)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;.AddProvider(Provider.Commerce)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddProductId(&quot;ProductId&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddVariantId(&quot;VarientId&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;.Build();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Assert.IsTrue(expectedProductReference == actualProductReference);
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [TestMethod]
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void UserReferenceBuilder_Build_ReturnsProperlyConstructedReference()
&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var expectedProductReference = Reference.Create(&quot;User://Microsoft/Dynamics/UserId&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var actualProductReference = new UserReferenceBuilder().AddPlatform(Platform.Microsoft)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddProvider(Provider.Dynamics)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddUserId(&quot;UserId&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.Build();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Assert.IsTrue(expectedProductReference == actualProductReference);
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;By employing these guidelines, developers can create reusable code to help construct references consistently. &lt;br /&gt;Additional information on Episerver Social Reference can be found at: &lt;br /&gt;&lt;a href=&quot;/link/a09c5c7c7ca54b9b8bcb06d846cf9031.aspx#referenceandIDs&quot;&gt;http://world.episerver.com/documentation/developer-guides/social/social_platform-overview/discovering-the-platform/#referenceandIDs&lt;/a&gt;.&lt;br /&gt;&amp;nbsp;&lt;/p&gt;</id><updated>2017-04-13T16:44:37.9900000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Implementing Membership Moderation using Episerver Social</title><link href="https://world.optimizely.com/blogs/benjamin-schaefer/dates/2017/3/implementing-membership-moderation/" /><id>&lt;p&gt;&lt;strong&gt;&amp;nbsp;Summary:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The goal of this tutorial is to outline how to implement moderation for group membership admission. The tutorial describes an interconnected relationship between four social services: groups, members, workflows and workflow items.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A working Episerver SocialAlloy site. This repo can be pulled from the &lt;a title=&quot;Github&quot; href=&quot;https://github.com/episerver/SocialAlloy&quot;&gt;Episerver Github page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example of Membership Moderation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;img alt=&quot;Image pic1.png&quot; src=&quot;/link/63a78add400147798543b2be66ac9e0a.aspx&quot; height=&quot;284&quot; width=&quot;272&quot; /&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The Silver Reseller GroupAdmissionBlock on the Silver Reseller CommunityPage in the SocialAlloy starter site has an existing workflow associated with it. All users who request to join this group are entered into membership moderation.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Image pic2.png&quot; src=&quot;/link/27c89a842678449eb904cf63800e7f42.aspx&quot; height=&quot;430&quot; width=&quot;641&quot; /&gt;&lt;/p&gt;
&lt;p&gt;A user has been entered into the workflow for the Silver Resellers Group. This group has an existing workflow associated&amp;nbsp;that has an initial state of “Pending.”&amp;nbsp;The two actions that can be taken on that state that are “Accept” and “Ignore”.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Image pic3.png&quot; src=&quot;/link/cce8baef29ed483ebaa0c2c83e49760e.aspx&quot; height=&quot;430&quot; width=&quot;645&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If the “Accept” action is taken, the member admission entity enters into a new state called “Accepted”. The available actions from this state are “Approve” and “Reject”.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;img alt=&quot;Image pic4.png&quot; src=&quot;/link/a723f5b64cb64e458c72f12085d9dcf1.aspx&quot; height=&quot;356&quot; width=&quot;630&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If the “Approve” action is taken, the member enters into a new state called “Approved”. No additional actions can be taken once this state is reached. This is one potential end to the community membership workflow. If a moderator had taken either the “Ignore” or “Reject” actions, the outcome to the member request would be “Rejected”.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Before Creating a Moderation Strategy&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It is important to clearly identify the business process that you are looking to translate into a workflow. The Social Framework provides methods to create and interact with a workflow. However, the relevant entities of a workflow can differ greatly between use cases.&lt;/p&gt;
&lt;p&gt;For this tutorial, the business process that has been outlined is the moderation of group membership. Within the SocialAlloy site, distinct constructs have been created to abstract the site specific models and logic from those of the Social Framework. Communities encapsulate the relevant information about a group. Communities allow for the membership of CommunityMembers (a class used to convey the details of a member). &amp;nbsp;Communities that desire a way to moderate the admission of CommunityMembers require the support of a CommunityMembershipWorkflow. The CommunityMembershipWorkflow model defines the essential components for CommityMembership moderation within the SocialAlloy site.&lt;/p&gt;
&lt;p&gt;Managing the business specific components with a clearly-defined process in advance of implementing the Social Framework will result in an uneventful and enjoyable development experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Building a workflow&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The SocialAlloy starter site is seeded with existing groups. These groups have corresponding workflows that allow for membership requests into the group to be moderated. An example of method used to add a workflow can be seen below:&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
        /// Adds a workflow to the underlying repository
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&quot;community&quot;&amp;gt;The community that will be associated with the workflow&amp;lt;/param&amp;gt;
        public void AddWorkflow(Community community)
        {
            // Define the transitions for workflow:            
            // Pending -&amp;gt; (Accept) -&amp;gt; Accepted
            //     |                      |-- (Approve) -&amp;gt; Approved
            //     |                       `- (Reject)  -&amp;gt; Rejected
            //      `---&amp;gt; (Ignore) -&amp;gt; Rejected

            var workflowTransitions = new List&amp;lt;WorkflowTransition&amp;gt;
            {
                new WorkflowTransition(new WorkflowState(&quot;Pending&quot;),  new WorkflowState(&quot;Accepted&quot;), new WorkflowAction(&quot;Accept&quot;)),
                new WorkflowTransition(new WorkflowState(&quot;Pending&quot;),  new WorkflowState(&quot;Rejected&quot;), new WorkflowAction(&quot;Ignore&quot;)),
                new WorkflowTransition(new WorkflowState(&quot;Accepted&quot;), new WorkflowState(&quot;Approved&quot;), new WorkflowAction(&quot;Approve&quot;)),
                new WorkflowTransition(new WorkflowState(&quot;Accepted&quot;), new WorkflowState(&quot;Rejected&quot;), new WorkflowAction(&quot;Reject&quot;))
            };

            // Save the new workflow with custom extension data which 
            // identifies the community it is intended to be associated with.
          &lt;br /&gt;            var membershipWorkflow = new Workflow(
                &quot;Membership: &quot; + community.Name,
                workflowTransitions,
                new WorkflowState(&quot;Pending&quot;)
            );
&lt;br /&gt;            var workflowExtension = new MembershipModeration { Group = community.Id };
            if (membershipWorkflow != null)
            {
                try
                {
                    this.workflowService.Add(membershipWorkflow, workflowExtension);
                }
                catch (SocialAuthenticationException ex)
                {
                    throw new SocialRepositoryException(&quot;The application failed to authenticate with Episerver Social.&quot;, ex);
                }
                catch (MaximumDataSizeExceededException ex)
                {
                    throw new SocialRepositoryException(&quot;The application request was deemed too large for Episerver Social.&quot;, ex);
                }
                catch (SocialCommunicationException ex)
                {
                    throw new SocialRepositoryException(&quot;The application failed to communicate with Episerver Social.&quot;, ex);
                }
                catch (SocialException ex)
                {
                    throw new SocialRepositoryException(&quot;Episerver Social failed to process the application request.&quot;, ex);
                }
            }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There are four separate sections to this method that are important to take note of.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The construction of the list of WorkflowTransitions.
&lt;ul&gt;
&lt;li&gt;The Workflow object takes a list of WorkflowTransitions. A WorkflowTransition describes the action required to move from one state to another within the workflow A WorkflowTransition consists of two WorkflowStates and one WorkflowAction. The WorkflowStates reflect where the entity in the workflow is coming from and where it is going to when the associated WorkflowAction is taken.&lt;/li&gt;
&lt;li&gt;The transitions for a workflow cannot contain duplicate entries with identical combinations of From and To state.&lt;/li&gt;
&lt;li&gt;An example of a WorkflowTransition is when an entity is in a “Pending” state and can be moved into an “Accepted” state by taking the WorkflowAction of “Accept”.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The construction of the Workflow object.
&lt;ul&gt;
&lt;li&gt;In addition to a list of WorkflowTransitions, the Workflow object needs a name and an initial state to be properly constructed. An initial WorkflowState is the starting state for any entity&amp;nbsp;entered into this workflow.&lt;/li&gt;
&lt;li&gt;It is important to note that the initial state of the workflow being added must match either the &lt;strong&gt;From&lt;/strong&gt; or the &lt;strong&gt;To&lt;/strong&gt; state of at least one of the workflow transitions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The construction of the MembershipModeration extension data.
&lt;ul&gt;
&lt;li&gt;The MembershipModeration extension data is used to persist relevant information about the group with which the workflow is associated.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The adding of the workflow and the workflow extension data to the Social system.
&lt;ul&gt;
&lt;li&gt;When using any Social Services, you must anticipate the possibility that a Social exception might be thrown. Organizations need to determine in advance how to handle Social exceptions.&lt;/li&gt;
&lt;li&gt;While there is a return value for a Workflow Service Add, this demo was designed to not use the persisted composite.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Adding a Moderated Member&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The GroupAdmissionBlock facilitates the addition of members to groups within SocialAlloy. When a group is created with membership moderation, the block internally calls the AddAModeratedMember method on the CommunityMemberModerationRepository when a user requests membership to the group.&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; /// &amp;lt;summary&amp;gt;
        /// Submits a membership request to the specified group&#39;s
        /// moderation workflow for approval.
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&quot;member&quot;&amp;gt;The member information for the membership request&amp;lt;/param&amp;gt;
        public void AddAModeratedMember(CommunityMember member)
        {
            // Define a unique reference representing the entity
            // under moderation. Note that this entity may be
            // transient or may not yet have been assigned a
            // unique identifier. Defining an item reference allows
            // you to bridge this gap.

            // For example: &quot;members:/{group-id}/{user-reference}&quot;

            var targetReference = this.CreateUri(member.GroupId, member.User);

            // Retrieve the workflow supporting moderation of
            // membership for the group to which the user is
            // being added.

            var moderationWorkflow = this.GetWorkflowFor(member.GroupId);

            // The workflow defines the intial (or &#39;start&#39;) state
            // for moderation.

            var initialState = moderationWorkflow.InitialState;

            // Create a new workflow item...

            var workflowItem = new WorkflowItem(
                WorkflowId.Create(moderationWorkflow.Id),      // ...under the group&#39;s moderation workflow
                new WorkflowState(initialState),               // ...in the workflow&#39;s initial state
                Reference.Create(targetReference)              // ...identified with this reference
            );

            var memberRequest = memberAdapter.Adapt(member);
                try
                {
                    this.workflowItemService.Add(workflowItem, memberRequest);
                }
                catch (SocialAuthenticationException ex)
                {
                    throw new SocialRepositoryException(&quot;The application failed to authenticate with Episerver Social.&quot;, ex);
                }
                catch (MaximumDataSizeExceededException ex)
                {
                    throw new SocialRepositoryException(&quot;The application request was deemed too large for Episerver Social.&quot;, ex);
                }
                catch (SocialCommunicationException ex)
                {
                    throw new SocialRepositoryException(&quot;The application failed to communicate with Episerver Social.&quot;, ex);
                }
                catch (SocialException ex)
                {
                    throw new SocialRepositoryException(&quot;Episerver Social failed to process the application request.&quot;, ex);
                }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The AddAModeratedMember method can be divided into four distinct sections:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Creation of a unique URI used to track the progression of a member being moderated for group admission.&lt;/li&gt;
&lt;li&gt;Retrieval of the workflow for the group to which a user is requesting membership.&lt;/li&gt;
&lt;li&gt;A Workflow item is constructed to encapsulate three important parts:
&lt;ul&gt;
&lt;li&gt;the unique identifier for the workflow associated with the group&lt;/li&gt;
&lt;li&gt;the initial state of the associated workflow&lt;/li&gt;
&lt;li&gt;the unique URI to track progression of an entity through the workflow&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The addition of the WorkflowItem to the Social system by using the WorkflowItem Service Add method. A WorkflowItem captures the state and data of an entity within a moderation workflow.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Interacting with existing workflows&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Below is an example of a Moderation View. This is a standard moderation interface that allows a user to select from a list of existing Workflows, displays existing membership requests for a workflow, and acts on available actions for each member request.&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; &amp;lt;div class=&quot;row&quot;&amp;gt;
        &amp;lt;div class=&quot;span12&quot;&amp;gt;
            &amp;lt;div&amp;gt;
                @using (Html.BeginForm(&quot;Index&quot;, &quot;Moderation&quot;, FormMethod.Get))
                {
                   &amp;lt;div&amp;gt;Choose a group to moderate:&amp;lt;/div&amp;gt;
                    &amp;lt;div&amp;gt;
                        @Html.DropDownListFor(x =&amp;gt; x.SelectedWorkflow,
                                              new SelectList(
                                                 Model.Workflows,
                                                  &quot;Id&quot;,
                                                  &quot;Name&quot;, 
                                                  Model.Workflows.First().Id), new { @class = &quot;MakeWide&quot; })
                    &amp;lt;/div&amp;gt;
                   &amp;lt;div&amp;gt;&amp;lt;input type=&quot;submit&quot; value=&quot;View&quot; /&amp;gt;&amp;lt;/div&amp;gt;
                }
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;row&quot;&amp;gt;
        &amp;lt;div class=&quot;span12&quot;&amp;gt;
            &amp;lt;div&amp;gt;
                &amp;lt;h4&amp;gt;Membership Requests&amp;lt;/h4&amp;gt;
                &amp;lt;p&amp;gt;
                    There are membership requests which may be pending your approval.
                &amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;table class=&quot;table table-striped&quot;&amp;gt;
                &amp;lt;thead&amp;gt;
                    &amp;lt;tr&amp;gt;
                        &amp;lt;th&amp;gt;User&amp;lt;/th&amp;gt;
                        &amp;lt;th&amp;gt;State&amp;lt;/th&amp;gt;
                        &amp;lt;th&amp;gt;Date&amp;lt;/th&amp;gt;
                        &amp;lt;th&amp;gt;Actions&amp;lt;/th&amp;gt;
                    &amp;lt;/tr&amp;gt;
                &amp;lt;/thead&amp;gt;
                @foreach (var item in Model.Items)
                {
                    &amp;lt;tr&amp;gt;
                        &amp;lt;td&amp;gt;
                            @item.UserName
                        &amp;lt;/td&amp;gt;
                        &amp;lt;td&amp;gt;
                            @item.State
                        &amp;lt;/td&amp;gt;
                        &amp;lt;td&amp;gt;
                            @item.Created
                        &amp;lt;/td&amp;gt;
                        &amp;lt;td&amp;gt;
                            @using (Html.BeginForm(&quot;Index&quot;, &quot;Moderation&quot;, FormMethod.Post))
                            {
                                &amp;lt;input type=&quot;hidden&quot; name=&quot;userId&quot; value=&quot;@Html.AttributeEncode(item.User)&quot; /&amp;gt;
                                &amp;lt;input type=&quot;hidden&quot; name=&quot;communityId&quot; value=&quot;@Html.AttributeEncode(item.Group)&quot; /&amp;gt;
                                &amp;lt;input type=&quot;hidden&quot; name=&quot;workflow&quot; value=&quot;@Html.AttributeEncode(Model.SelectedWorkflow.Id)&quot; /&amp;gt;

                                if (item.Actions.Count() == 0)
                                {
                                    &amp;lt;p class=&quot;muted&quot;&amp;gt;No actions available&amp;lt;/p&amp;gt;
                                }

                                foreach (var action in item.Actions)
                                {
                                    &amp;lt;input type=&quot;submit&quot; name=&quot;workflowAction&quot; value=&quot;@action&quot; /&amp;gt;
                                }
                            }
                        &amp;lt;/td&amp;gt;
                    &amp;lt;/tr&amp;gt;
                }
            &amp;lt;/table&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The CommunityMemberModerationRepository&amp;nbsp;was&lt;span&gt; built with&lt;/span&gt; a Get method that returns &lt;span&gt;everything you need to build a moderation screen.&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;        /// &amp;lt;summary&amp;gt;
        /// Returns a view model supporting the presentation of group
        /// membership moderation information.
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&quot;workflowId&quot;&amp;gt;Identifier for the selected membership moderation workflow&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;View model of moderation information&amp;lt;/returns&amp;gt;
        public CommunityModerationViewModel Get(string workflowId)
        {
            try
            {
                // Retrieve a collection of all workflows in the system with MembershipModeration extension data.
                var allWorkflows = this.GetWorkflows();
                // Retrieve the workflow specified as the selected one.
                // If no workflow is selected, default to the first
                // available workflow.

                var selectedWorkflow = string.IsNullOrWhiteSpace(workflowId)
                    ? allWorkflows.FirstOrDefault()
                    : allWorkflows.FirstOrDefault(w =&amp;gt; w.Id.ToString() == workflowId);

                // Retrieve the current state for all membership requests
                // under the selected moderation workflow.

                var currentWorkflowItems = this.GetWorkflowItemsFor(selectedWorkflow);
            &lt;br /&gt;                var workflowItemAdapter = new CommunityMembershipRequestAdapter(selectedWorkflow, this.userRepository);
            &lt;br /&gt;                return new CommunityModerationViewModel
                {
                    Workflows = allWorkflows.Select(workflowAdapter.Adapt),
                    SelectedWorkflow = workflowAdapter.Adapt(selectedWorkflow),
                    Items = currentWorkflowItems.Select(workflowItemAdapter.Adapt)
                };
            }
            catch (SocialAuthenticationException ex)
            {
                throw new SocialRepositoryException(&quot;The application failed to authenticate with Episerver Social.&quot;, ex);
            }
            catch (MaximumDataSizeExceededException ex)
            {
                throw new SocialRepositoryException(&quot;The application request was deemed too large for Episerver Social.&quot;, ex);
            }
            catch (SocialCommunicationException ex)
            {
                throw new SocialRepositoryException(&quot;The application failed to communicate with Episerver Social.&quot;, ex);
            }
            catch (SocialException ex)
            {
                throw new SocialRepositoryException(&quot;Episerver Social failed to process the application request.&quot;, ex);
            }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The retrieval of the CommunityModerationViewModel consists of three major parts.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Retrieving all available workflows in the system by calling the GetWorkflows method in the CommMakeWide&quot; }) &amp;lt;/div&amp;gt; &amp;lt;div&amp;gt;&amp;lt;input type=&quot;submit&quot; value=&quot;View&quot; /&amp;gt;&amp;lt;/div&amp;gt; } unityMemberModerationRepository. This method uses the Workflow Service Get method. When all workflows are returned, it is possible to select the one that matches the workflow id provided. Also, by retrieving all workflows, allow the UI to display available workflows in the moderation selection dropdown.&lt;/li&gt;
&lt;li&gt;GetWorkflowItemsFor method is then called and invokes the WorkflowItem Service Get method using &lt;a title=&quot;CompositeCriteria&quot; href=&quot;/link/a09c5c7c7ca54b9b8bcb06d846cf9031.aspx#composite_criteria&quot; target=&quot;_blank&quot;&gt;CompositeCriteria&lt;/a&gt;. CompositeCriteria encapsulates the specifications by which platform entities, composed with extension data, should be retrieved. The CompositeCriteria used here look similar to this:&lt;br /&gt;&lt;br /&gt;
&lt;div&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;   var criteria = new CompositeCriteria&amp;lt;WorkflowItemFilter, AddMemberRequest&amp;gt;
   {
             Filter = new WorkflowItemFilter
             {
                 ExcludeHistoricalItems = true,      // Include only the current state for the requests
                 Workflow = workflow.Id,             // Include only items for the selected group&#39;s workflow
              },
              PageInfo = new PageInfo { PageSize = 30 },   // Limit to 30 items&lt;br /&gt;    };  
    // Order the results alphabetically by their state and then
    // by the date on which they were created.

    criteria.OrderBy.Add(new SortInfo(WorkflowItemSortFields.State, true));
    criteria.OrderBy.Add(new SortInfo(WorkflowItemSortFields.Created, true));                  &lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;The Get request only returns&amp;nbsp;WorkflowItems in their current state that are associated with the requested workflow.&lt;/li&gt;
&lt;li&gt;The values being returned are ordered by state name&amp;nbsp;then by WorkflowItem creation date.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;3. Populating the ModerationViewModel requires:&lt;/p&gt;
&lt;ul&gt;
&lt;ul&gt;
&lt;li&gt;all workflows that were retrieved in step 1&lt;/li&gt;
&lt;li&gt;the populated selected workflow that was requested&lt;/li&gt;
&lt;li&gt;all&amp;nbsp;WorkflowItems associated with the requested Workflow&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;p&gt;** It is important to note that this example uses an adapter class that works with available objects to produce a different, client suitable object that the view can interpret.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Moderate a workflowitem&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When looking to moderate a specific workflow item, an interface needs to display available items to moderate and actions that may be taken on them. This shows an example of how displaying those items and actions could be done.&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;  @foreach (var item in Model.Items)
  {
         &amp;lt;tr&amp;gt;
             &amp;lt;td&amp;gt;
                @item.UserName
             &amp;lt;/td&amp;gt;
             &amp;lt;td&amp;gt;
                 @item.State
              &amp;lt;/td&amp;gt;
              &amp;lt;td&amp;gt;
                  @item.Created
              &amp;lt;/td&amp;gt;
              &amp;lt;td&amp;gt;
                    @using (Html.BeginForm(&quot;Index&quot;, &quot;Moderation&quot;, FormMethod.Post))
                    {
                        &amp;lt;input type=&quot;hidden&quot; name=&quot;userId&quot; value=&quot;@Html.AttributeEncode(item.User)&quot; /&amp;gt;
                        &amp;lt;input type=&quot;hidden&quot; name=&quot;communityId&quot; value=&quot;@Html.AttributeEncode(item.Group)&quot; /&amp;gt;
                        &amp;lt;input type=&quot;hidden&quot; name=&quot;workflow&quot; value=&quot;@Html.AttributeEncode(Model.SelectedWorkflow.Id)&quot; /&amp;gt;
&lt;br /&gt;                         if (item.Actions.Count() == 0)
                         {
                            &amp;lt;p class=&quot;muted&quot;&amp;gt;No actions available&amp;lt;/p&amp;gt;
                         }

                         foreach (var action in item.Actions)
                         {
                                &amp;lt;input type=&quot;submit&quot; name=&quot;workflowAction&quot; value=&quot;@action&quot; /&amp;gt;
                         }
                    }
               &amp;lt;/td&amp;gt;
         &amp;lt;/tr&amp;gt;
   }&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Once a moderator has taken an action on an item in moderation, that action must be managed in a meaningful way within the Social Framework. For this tutorial, a Moderate method was created in the CommunityMembershipModerationRepository to account for what should happen when an item in moderation is acted upon.&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;        /// &amp;lt;summary&amp;gt;
        /// Takes action on the specified workflow item, representing a
        /// membership request.
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&quot;workflowId&quot;&amp;gt;The id of the workflow &amp;lt;/param&amp;gt;
        /// &amp;lt;param name=&quot;action&quot;&amp;gt;The moderation action to be taken&amp;lt;/param&amp;gt;
        /// &amp;lt;param name=&quot;userId&quot;&amp;gt;The unique id of the user under moderation.&amp;lt;/param&amp;gt;
        /// &amp;lt;param name=&quot;communityId&quot;&amp;gt;The unique id of the community to which membership has been requested.&amp;lt;/param&amp;gt;
        public void Moderate(string workflowId, string action, string userId, string communityId)
        {
            var membershipRequest = GetMembershipRequest(userId, communityId);
            var populatedWorkflowId = WorkflowId.Create(workflowId);

            var requestReference = Reference.Create(CreateUri(membershipRequest.Group, membershipRequest.User));

            try
            {
                var transitionToken = this.workflowService.BeginTransitionSession(populatedWorkflowId, requestReference);
                try
                {
                    // Retrieve the moderation workflow associated with
                    // the item to be acted upon.

                    var workflow = this.workflowService.Get(populatedWorkflowId);

                    // Leverage the workflow to determine what the
                    // resulting state of the item will be upon taking 
                    // the specified action.

                    //retrieve the current state of the workflow item once the begintransitionsession begins.
                    var filter = new WorkflowItemFilter { Target = requestReference };
                    var criteria = new Criteria&amp;lt;WorkflowItemFilter&amp;gt; { Filter = filter };
                    var workflowItem = this.workflowItemService.Get(criteria).Results.Last();

                    // Example: Current State: &quot;Pending&quot;, Action: &quot;Approve&quot; =&amp;gt; Transitioned State: &quot;Approved&quot;
                    var transitionedState = workflow.Transition(workflowItem.State, new WorkflowAction(action));

                    var subsequentWorkflowItem = new WorkflowItem(
                        workflow.Id,
                        transitionedState,
                        requestReference
                    );

                    this.workflowItemService.Add(subsequentWorkflowItem, membershipRequest, transitionToken);

                    // Perform any application logic given the item&#39;s
                    // new state.

                    if (this.IsApproved(subsequentWorkflowItem.State))
                    {
                        memberRepository.Add(memberAdapter.Adapt(membershipRequest));
                    }
                }
                finally
                {
                    this.workflowService.EndTransitionSession(transitionToken);
                }
            }
            catch (SocialAuthenticationException ex)
            {
                throw new SocialRepositoryException(&quot;The application failed to authenticate with Episerver Social.&quot;, ex);
            }
            catch (MaximumDataSizeExceededException ex)
            {
                throw new SocialRepositoryException(&quot;The application request was deemed too large for Episerver Social.&quot;, ex);
            }
            catch (SocialCommunicationException ex)
            {
                throw new SocialRepositoryException(&quot;The application failed to communicate with Episerver Social.&quot;, ex);
            }
            catch (SocialException ex)
            {
                throw new SocialRepositoryException(&quot;Episerver Social failed to process the application request.&quot;, ex);
            }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The Moderate method takes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a unique identifier for the associated workflow&lt;/li&gt;
&lt;li&gt;the action that was taken by the moderator&lt;/li&gt;
&lt;li&gt;a unique identifier for the user requesting membership to a community&lt;/li&gt;
&lt;li&gt;the unique identifier for the associated community&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The method can be broken down into 8 simple steps.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The construction of a unique URI (the same URI from the step 1 in the AddAModeratedMember method) for the workflow request.&lt;/li&gt;
&lt;li&gt;The BeginTransitionSession method in the WorkflowService must be called in advance of any WorkflowItem transition.
&lt;ul&gt;
&lt;li&gt;Requests exclusive access to add workflow items for a target within a particular workflow. If another client already has secured access to that target, a TransitionSessionDeniedException&amp;nbsp;is thrown.&lt;/li&gt;
&lt;li&gt;The TransitionSessionToken returned in this operation&amp;nbsp;is used in the addition of a new WorkflowItem.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Retrieval of the most recent WorkflowItem by the Workflow Service by using the Get method.&lt;/li&gt;
&lt;li&gt;Using the Workflow Transition method, the transitioned state is constructed from the current WorkflowItem state and the requested action.&lt;/li&gt;
&lt;li&gt;Construction of a new WorkflowItem with the workflow identifier, the transitioned state, and the unique URI (constructed in step 1).&lt;/li&gt;
&lt;li&gt;The new WorkflowItem is added to the system with the populated AddMemberRequest and TransitionSessionToken.&lt;/li&gt;
&lt;li&gt;This tutorial&amp;nbsp;has a conditional check when moderating to see if the new WorkflowItem is in a state of approved (one possible end of the workflow). Only when the admission state has reached &quot;accepted&quot; is the member added to the MemberRepository.&lt;/li&gt;
&lt;li&gt;Once the Add method has successfully updated the state of the admission request and no error is encountered, the EndTransitionSession method in the WorkflowService should be called.
&lt;ul&gt;
&lt;li&gt;The EndTransitionSession method is passed the TransitionSessionToken that was constructed in step 2.&lt;/li&gt;
&lt;li&gt;This method ends the transition session, relinquishing exclusive access to add workflow items to the target.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Essential files for Group Membership Moderation Tutorial&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File Type&lt;/strong&gt;: Controller&lt;br /&gt;&lt;strong&gt;Location&lt;/strong&gt;: SocialAlloy/Social/Controllers/&lt;br /&gt;&lt;strong&gt;Name / Descriptio&lt;/strong&gt;n:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ModerationController.cs – used to update the CommunityModerationViewModel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;File Type&lt;/strong&gt;: Model&lt;br /&gt;&lt;strong&gt;Location&lt;/strong&gt;: SocialAlloy/Social/Models/&lt;br /&gt;&lt;strong&gt;Name/ Description&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CommunityMembershipWorkflow.cs - encapsulates SocialAlloy specific details of a Group Workflow&lt;/li&gt;
&lt;li&gt;CommunityModerationViewModel.cs - relays moderation details from the ModerationController to the Moderation view&lt;/li&gt;
&lt;li&gt;Community.cs - describes a group model used by the SocialAlloy site.&lt;/li&gt;
&lt;li&gt;CommunityMember.cs – describes a member model used by the SocialAlloy site&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;File Type&lt;/strong&gt;: View:&lt;br /&gt;&lt;strong&gt;Location&lt;/strong&gt;: SocialAlloy/Views/Social/Moderation&lt;br /&gt;&lt;strong&gt;Name / Description:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Index.cshtml – Generates outputs to the user based upon the values of the CommunityModerationViewModel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;File Type&lt;/strong&gt;: Repository&lt;br /&gt;&lt;strong&gt;Location&lt;/strong&gt;: SocialAlloy/Social/Repositories/Moderation&lt;br /&gt;&lt;strong&gt;Name / Description:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ICommunityMemberModerationRepository.cs – The interface describing operations that manage community membership moderation&lt;/li&gt;
&lt;li&gt;CommunityMemberModerationRepository.cs - Implements operations that manage community membership moderation with the Episerver Social Framework&lt;/li&gt;
&lt;li&gt;ICommunityMemberRepostiory.cs – The interface that describes a component capable of persisting and retrieving community member data&lt;/li&gt;
&lt;li&gt;CommunityMemberRepository.cs - Persists and retrieves community member data to and from the Episerver Social Framework&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;* Repositories in the SocialAlloy site are responsible for the sole point of interaction with the Social API. Social constructs have been abstracted away by site specific entities in order to separate the business logic of the site from the Social API integration points.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File Type&lt;/strong&gt;: Extension Data&lt;br /&gt;&lt;strong&gt;Location&lt;/strong&gt;: EPiServer.SocialAlloy.ExtensionData/Membership&lt;br /&gt;&lt;strong&gt;Name / Description:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AddMemberRequest.cs - a serializable class representing a request for membership to a group. It is intended to support the moderation process around group membership.&lt;/li&gt;
&lt;li&gt;MemberExtensionData.cs- Custom extension data used in saving member details for members associated with groups.&lt;/li&gt;
&lt;li&gt;MembershipModeration.cs - Intended to identify the relationship between a moderation workflow and the group which it supports for moderating membership requests. This serves as an extension data added to workflows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;* It is important to note that the extension data for the SocialAlloy site has been moved to its own project by design. Compartmentalizing extension data and adding that project into a site solution ideally make namespaces more stable and less susceptible to changing over time. Additionally, by separating the extension data, it should be very easy for a user to reference the same POCOs in their staging site that they are referencing in their production site.&lt;/p&gt;</id><updated>2017-03-17T15:10:16.4230000Z</updated><summary type="html">Blog post</summary></entry></feed>