You might have used Alternative Publishing in the past, which can be a very handy feature. By using it you can publish any type of content as feed and/or import into any module. At times, there may be a situation where you would need to set custom field values in the News module. This cannot be done through the UI, but we can make this work with some code. Let’s suppose we have the following custom fields in our News module that we need to set with the values in feed:
- Thumbnail
- IsExternalSource
- Tags (this is a built-in field but it’s not imported out of the box)
Take these English and Arabic feeds as an example from an external source.
Keep in mind that it’s one of the challenges to create translations of news items from two different feed URLs. Each <item> tag in the feed represents information of news items differentiating with a <guid> tag. So, items in English and Arabic with the same GUID represent the same news with a different translation.
Now we need to override RSSInboundPipe which is used to import items as a Sitefinity content type. Here, we will add our custom logic to extract values from non-standard tags in the feed (like thumbnailUrl), and add those to WrapperObject.
RssInboundPipeCustom.cs
using System; using System.Collections.ObjectModel; using System.Linq; using System.ServiceModel.Syndication; using System.Xml.Linq; using Telerik.Sitefinity.Publishing; using Telerik.Sitefinity.Publishing.Pipes; public class RssInboundPipeCustom : RSSInboundPipe { public override WrapperObject ConvertToWraperObject(System.ServiceModel.Syndication.SyndicationItem item) { WrapperObject obj = new WrapperObject(null); obj.MappingSettings = this.PipeSettings.Mappings; obj.Language = this.PipeSettings.LanguageIds.FirstOrDefault(); var feedItem = item as SyndicationItem; string id = feedItem.Id.Replace("urn:uuid:", ""); obj.AddProperty(PublishingConstants.FieldItemId, new Guid(id)); obj.AddProperty(PublishingConstants.FieldOriginalContentId, new Guid(id)); obj.AddProperty(PublishingConstants.FieldItemHash, GenerateItemHash(item, string.Empty)); obj.AddProperty(PublishingConstants.FieldTitle, feedItem.Title.Text); obj.AddProperty(PublishingConstants.FieldPublicationDate, feedItem.PublishDate.UtcDateTime); obj.AddProperty(PublishingConstants.FieldContent, ((TextSyndicationContent)feedItem.Content).Text); if (feedItem.Authors.Count > 0) { var author = feedItem.Authors[0]; obj.AddProperty(PublishingConstants.FieldOwnerEmail, author.Email); obj.AddProperty(PublishingConstants.FieldOwnerFirstName, author.Name); } foreach (SyndicationElementExtension extension in item.ElementExtensions) { XElement element = extension.GetObject<XElement>(); if (element.Name.LocalName == "thumbnail") { obj.SetOrAddProperty("Thumbnail", element.Value); } else if (element.Name.LocalName == "summary") { obj.SetOrAddProperty("Summary", element.Value); } } var tags = new Collection<SyndicationCategory>(); var tagElements = item.ElementExtensions.Select(i => i.GetObject<XElement>()).Where(i => i.Name.LocalName == "tag"); if (tagElements != null) { foreach (var tag in tagElements) { tags.Add(new SyndicationCategory(tag.Value)); } obj.AddProperty(PublishingConstants.FieldTags, tags); } obj.AdditionalProperties.Add("IsExternalSource", true); return obj; } protected override string GenerateItemHash(SyndicationItem item, string feedUrl) { var hasher = new System.Security.Cryptography.SHA1CryptoServiceProvider(); byte[] originalBytes = System.Text.Encoding.UTF8.GetBytes(item.Id.ToString()); byte[] encodedBytes = hasher.ComputeHash(originalBytes); return Convert.ToBase64String(encodedBytes); } }
Notice a method named GenerateItemHash above? This will ensure the items in different feeds are actually the same (with different translations) if the GUID matches.
Method SetOrAddProperty will not add value yet. We will have to override the ContentOutboundPipe class as well to make sure the values are set to extra fields.
ContentOutboundPipeCustom.cs
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.ServiceModel.Syndication; using Telerik.Sitefinity.Events.Model; using Telerik.Sitefinity.Model; using Telerik.Sitefinity.News.Model; using Telerik.Sitefinity.Publishing; using Telerik.Sitefinity.Publishing.Pipes; using Telerik.Sitefinity.RelatedData; using Telerik.Sitefinity.Taxonomies.Model; using System.Text.RegularExpressions; using Telerik.Sitefinity.Data.Linq.Dynamic; public class ContentOutboundPipeCustom : ContentOutboundPipe { protected override void SetPropertiesThroughPropertyDescriptor(Telerik.Sitefinity.Model.IContent item, Telerik.Sitefinity.Publishing.WrapperObject wrapperObj) { base.SetPropertiesThroughPropertyDescriptor(item, wrapperObj); var wrapperObject = (WrapperObject)wrapperObj.WrappedObject; if (item is NewsItem) { var newsItem = item as NewsItem; #region Thumbnail if (wrapperObject.AdditionalProperties.ContainsKey("Thumbnail")) { var thumbnailUrl = wrapperObject.AdditionalProperties["Thumbnail"].ToString(); var existingThumbnail = newsItem.GetRelatedItems("Thumbnail").FirstOrDefault(); if (existingThumbnail == null && newsItem.DoesFieldExist("Thumbnail") && !string.IsNullOrEmpty(thumbnailUrl)) { var imageAlbum = Helper.GetOrCreateGetAlbum("Imported News Album"); var imageStream = Helper.GetStreamFromUrl(thumbnailUrl); if (imageStream != null && imageStream.Length > 0) { string imageTitle = Path.GetFileNameWithoutExtension(thumbnailUrl); string imageExtension = Path.GetExtension(thumbnailUrl); imageExtension = imageExtension.Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries).First(); var image = Helper.CreateImage(imageAlbum.Id, imageTitle, imageStream, imageExtension, new List<string> { "en", "ar" }); newsItem.CreateRelation(image, "Thumbnail"); } } } #endregion #region Tags if (wrapperObject.AdditionalProperties.ContainsKey("Tags")) { var tagsObj = wrapperObject.AdditionalProperties["Tags"]; if (tagsObj is Collection<SyndicationCategory>) { var tags = (Collection<SyndicationCategory>)tagsObj; if (tags != null) { var taxons = tags.Select(i => i.Name).ToArray(); var newTaxons = Helper.CreateFlatTaxons("Tags", taxons); if (newTaxons != null) { newsItem.Organizer.Clear("Tags"); newsItem.Organizer.AddTaxa("Tags", newTaxons.Select(i => i.Id).ToArray()); } } } } #endregion #region IsExternalSource if (newsItem.DoesFieldExist("IsExternalSource")) { newsItem.SetValue("IsExternalSource", true); } #endregion } } }
Note: You can find the Helper class here.
Register custom inbound and outbound pipes in Global.asax.cs.
protected void Application_Start(object sender, EventArgs e) { Bootstrapper.Initialized += OnBootstrapperInitialized; } protected static void OnBootstrapperInitialized(object sender, ExecutedEventArgs e) { if (e.CommandName == "Bootstrapped") { PublishingSystemFactory.UnregisterPipe(RSSInboundPipe.PipeName); PublishingSystemFactory.RegisterPipe(RSSInboundPipe.PipeName, typeof(RssInboundPipeCustom)); PublishingSystemFactory.UnregisterPipe(ContentOutboundPipe.PipeName); PublishingSystemFactory.RegisterPipe(ContentOutboundPipe.PipeName, typeof(ContentOutboundPipeCustom)); } }
Now, all we need to do is to set up inbound using Sitefinity’s backend. So, log on to dashboard and go to Administration > Alternative Publishing.
- Click ‘Create a feed’.
- Enter title ‘News Inbound Feed’.
- Remove default item from ‘Content to include’ section and click ‘Add another content type’.
- Enter the following settings for English and Arabic (or any other language). Editing mapping is not required.
- Remove the default item from ‘Publish as…’ section and click ‘Add more…’.
- Enter the following settings for English and Arabic (or any other language). Editing mapping is not required.
- Your feed settings should look like this.
Note: Please change feed URL to actual one.
And you’re done!
The post Import multilingual News from external RSS feed in Sitefinity appeared first on Falafel Software Blog.