Sitecore Experience Accelerator (SXA): Creating a Site - Part 3.2 Custom Modules - Social Feed
Configuration
To create a custom module from scratch the first step is very similar to what we did in Part 3.1 (Add Module)
Once the module has been added the next step is to manually create the different items that were created automatically in the previous blog post. i.e.
- Templates
- Branches
- Renderings
- Settings
Templates
The first item we need to add is the data templates. Go to Templates/Features/ITWORX/Social Feed
Create a template that has the base /sitecore/templates/System/Templates/Template and has the base template Folder and name it Social Feed Folder
Next create a template for the Social Feeds that inherits from Standard Template. The template should contain the access tokens for the different social media channels, a title for the social feed
Create a Rendering Parameters folder and add configurations for each social media whether it should be visible or not
Renderings
Next go to renderings/features/ITWORX/social feed and insert a controller rendering. We will discuss how to write the controller later in this article but for now add the fully qualified name of the controller class that you will create (ITWORX.SXA.Feature.SocialFeed.Controllers.SocialfeedsController,ITWORX.SXA.Feature.SocialFeed)
- Add Controller Action as Index
- Configure the Rendering Parameters to the rendering parameters you created, i.e. Templates/Feature/ITWORX/Social Feed/Rendering Parameters/Social Feed
- Add Data Source as query:$site/*[@@name='Data']/*[@@templatename='Social Feed Folder']|query:$sharedSites/*[@@name='Data']/*[@@templatename='Social Feed Folder']. This states that the data source will reside inside Data/Social Feed Folder
- Choose Data Source Template to be /sitecore/templates/Feature/ITWORX/Social Feed/Social Feed (The template we created)
- Add a Rendering css class "social-media-container"
- Add a Renderings View Path "~/Views/Components/FeaturedSocialFeeds.cshtml" (we will create it later)
Branches
Add the Available Renderings and Rendering Variant branches
System / Settings
Add the branches to the site setup
Development
Let's now start preparing the back end code.
- Create an MVC empty project
- Add the Sitecore References. My recommendation is use the Sitecore Nuget and add the following: Sitecore.Kernel, Sitecore.Mvc
- I usually recommend to create a project structure similar to the below
So lets go through the above folder structure explaining what each item is.
App_Config
Within the App_Config we add a Feature / Include folder and add a config file to register our component. The config file will add a configurator pointing to the IoC Register class
<configuration xmlns:patch="https://www.sitecore.net/xmlconfig/">
<sitecore>
<services>
<configurator type="ITWORX.SXA.Feature.SocialFeed.Pipeline.IoC.RegisterSocialFeedsServices,ITWORX.SXA.Feature.SocialFeed" />
</services>
</sitecore>
</configuration>
Pipeline
I usually add a folder called IoC (Inversion of Control) within the Pipeline folder what it has is a registration for the social feed item and social feed list repositories
using Microsoft.Extensions.DependencyInjection;
using Sitecore.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace ITWORX.SXA.Feature.SocialFeed.Pipeline.IoC
{
public class RegisterSocialFeedsServices : IServicesConfigurator
{
public void Configure(IServiceCollection serviceCollection)
{
serviceCollection.AddTransient<ISocialFeedItemRepository, SocialFeedItemRepository>();
serviceCollection.AddTransient<ISocialFeedListRepository, SocialFeedListRepository>();
}
}
}
Note that this will do a build error until the models are added, but what this ultimately does is adds to the service collection the implementation of the Social Feed Repositories
Models
The models as with any MVC project contain the Models, in our scenario we have a list model and an item model
List Model
public class SocialFeedListModel : VariantListsRenderingModel
{
public List<SocialFeedItemModel> SocialFeedListItems { get; set; }
public List<SocialFeedItemModel> TwitterFeedListItems { get; set; }
public List<SocialFeedItemModel> InstagramFeedListItems { get; set; }
public List<SocialFeedItemModel> FacebookFeedListItems { get; set; }
public List<SocialFeedItemModel> YoutubeListItems { get; set; }
public List<SocialFeedItemModel> LinkedInListItems { get; set; }
public string TwitterRedirectURLTitle { get; set; }
public string TwitterAuth { get; set; }
public string TwitterScreenName { get; set; }
public string InstagramAccessToken { get; set; }
public string TwitterRedirectURL { get; set; }
public string Title { get; set; }
public string InstagramRedirectURL { get; set; }
public string InstagramRedirectURLTitle { get; set; }
public string FacebookRedirectURL { get; internal set; }
public string FacebookRedirectURLTitle { get; internal set; }
public string YouTubeRedirectURL { get; internal set; }
public string YoutubeRedirectURLTitle { get; internal set; }
public string LinkedInRedirectURL { get; internal set; }
public string LinkedInRedirectURLTitle { get; internal set; }
}
Item Model
public class SocialFeedItemModel : VariantsRenderingModel
{
public string Text { get; set; }
public string ScreenName { get; set; }
public string CreatedAt { get; set; }
public string URL { get; internal set; }
public string Type { get; internal set; }
public string redirectURL { get; set; }
}
As you can see they inherit from VariantListsRenderingModel and VariantsRenderingModel respectively
Controllers
The social feeds controller is quite simple, since the repository does all the heave lifting. IT inherits from PaginableController and overrides the GetModel function. The GetModel then instantiates the List Repository and invokes its GetModel
public class SocialfeedsController : PaginableController
{
protected ISocialFeedItemRepository SocialFeedItemRepository
{ get; set; }
protected ISocialFeedListRepository SocialFeedListRepository
{ get; set; }
public SocialfeedsController()
{
}
public SocialfeedsController(ISocialFeedListRepository Repository)
{
SocialFeedListRepository = Repository;
}
protected override object GetModel()
{
//string test = null;
//test.ToLower();
SocialFeedListRepository = new SocialFeedListRepository();
return SocialFeedListRepository.GetModel();
}
}
Repositories
The repositories has two interfaces and two classes for item and list
ISocialFeedItemRepository
The ISocialFeedItemRepository is quite basic since all it does is inherit from IVariantsRepository
public interface ISocialFeedItemRepository : IVariantsRepository
{
}
ISocialFeedListRepository
The ISocialFeedListRepository again doesn't have any functions but inherits from quite a few items
public interface ISocialFeedListRepository : IModelRepository, IControllerRepository, IAbstractRepository<IRenderingModelBase>
{
}
SocialFeedItemRepository
The social feed item repository inherits from VariantsRepository and ISocialFeedItemRepository, and overrides the GetMode by creating an instance of the model and invoking the FillBaseProperties of the model. What this does is fills the inherited fields of the Model
public class SocialFeedItemRepository : VariantsRepository, ISocialFeedItemRepository
{
public SocialFeedItemRepository()
{ }
public override IRenderingModelBase GetModel()
{
SocialFeedItemModel model = new SocialFeedItemModel();
FillBaseProperties(model);
return model;
}
}
SocialFeedListRepository
The SocialFeedListRepository is where most of the code is written.
public class SocialFeedListRepository : ListRepository, ISocialFeedListRepository, IModelRepository, IControllerRepository, IAbstractRepository<IRenderingModelBase>
{
private IEnumerable<SocialFeedItemModel> _socialFeedListItems;
public IEnumerable<SocialFeedItemModel> SocialFeedListItems
{
get
{
List<SocialFeedItemModel> socialFeeds = new List<SocialFeedItemModel>();
//Removed code to fill in the Social Feed, this is just a request through the REST Endpoint
return _socialFeedListItems;
}
}
public override IRenderingModelBase GetModel()
{
//instanitate the social feed list model
SocialFeedListModel model = new SocialFeedListModel();
model.Title = GetFieldValue("Title");
model.TwitterAuth = GetFieldValue("TwitterAuth");
model.TwitterScreenName = GetFieldValue("TwitterScreenName");
model.InstagramAccessToken = GetFieldValue("InstagramAccessToken");
model.TwitterRedirectURL = GetFieldValue("TwitterRedirectURL");
model.TwitterRedirectURLTitle = GetFieldValue("TwitterRedirectURLTitle");
model.InstagramRedirectURL = GetFieldValue("InstagramRedirectURL");
model.InstagramRedirectURLTitle = GetFieldValue("InstagramRedirectURLTitle");
model.FacebookRedirectURL = GetFieldValue("FacebookRedirectURL");
model.FacebookRedirectURLTitle = GetFieldValue("FacebookRedirectURLTitle");
model.YouTubeRedirectURL = GetFieldValue("YouTubeRedirectURL");
model.YoutubeRedirectURLTitle = GetFieldValue("YoutubeRedirectURLTitle");
model.LinkedInRedirectURL = GetFieldValue("LinkedInRedirectURL");
model.LinkedInRedirectURLTitle = GetFieldValue("LinkedInRedirectURLTitle");
this.FillBaseProperties((object)model);
//Get the different social feeds
model.SocialFeedListItems = this.SocialFeedListItems.ToList();
model.TwitterFeedListItems = this.TwitterFeedListItems.ToList();
model.InstagramFeedListItems = this.InstagramFeedListItems.ToList();
model.FacebookFeedListItems = this.FacebookFeedListItems.ToList();
model.YoutubeListItems = this.YoutubeFeedListItems.ToList();
model.LinkedInListItems = this.LinkedInListItems.ToList();
return (IRenderingModelBase)model;
}
//Do a null check before returning the field value
private string GetFieldValue(string fieldName)
{
if(Rendering!=null && Rendering.DataSourceItem!=null && Rendering.DataSourceItem.Fields[fieldName]!=null && Rendering.DataSourceItem.Fields[fieldName].Value!=null)
{
return Rendering.DataSourceItem.Fields[fieldName].Value;
}
return "";
}
}
Views
Finally we will create a view, the view will loop through the different list items and loop through the rendering variant for them.