In my previous post I created a custom Content Editor image field in the Sitecore Experience Platform. This custom image field gives content authors the ability to download an image from outside of their Sitecore instance; save the image to the Media Library; and then map that resulting Media Library Item to the custom Image field on an Item in the content tree.
Building that solution was a great way to spend a Friday night (and even the following Saturday morning) — though I bet some people would argue watching cat videos on YouTube might be better way to spend a Friday night — and even gave me the opportunity to share that solution with you guys.
After sharing this post on Twitter, Sitecore MVP Kam Figy replied to that tweet with the following:
This gave me an idea: why not modify the solution from my previous post to give the ability to download a random image from Giphy via their the API?
You might be asking yourself “what is this Giphy thing?” Giphy is basically a site that allows users to upload images — more specifically animated GIFs — and then associate those uploaded images with tags. These tags are used for finding images on their site and also through their API.
You might be now asking “what’s the point of Giphy?” The point is to have fun and share a laugh; animated GIFs can be a great way of achieving these.
Some smart folks out there have built integrations into other software platforms which give users the ability pull images from the Giphy API. An example of this can be seen in Slack messaging application.
As a side note, if you aren’t on the Sitecore Community Slack, you probably should be. This is the fastest way to get help, share ideas and even have some good laughs from close to 1000 Sitecore developers, architects and marketers from around the world in real-time. If you would like to join the Sitecore Community Slack, please let me know and I will send you an invite though please don’t ask for an invite in comments section below on this post. Instead reach out to me on Twitter: @mike_i_reynolds. You can also reach out to Sitecore MVP Akshay Sura: @akshaysura13.
Here’s an example of me calling up an image using some tags in one of the channels on the Sitecore Community Slack using the Giphy integration for Slack:
There really isn’t anything magical about the Giphy API — all you have to do is send an HTTP request with some query string parameters. Giphy’s API will then give you a response in JSON:
Before I dig into the solution below, I do want to let you know I will not be talking about all of the code in the solution. Most of the code was repurposed from my previous post. If you have not read my previous post, please read it before moving forward so you have a full understanding of how this works.
Moreover, do note there is probably no business value in using the following solution as is — it was built for fun on another Friday night and Saturday morning. 😉
To get data out of this JSON response, I decided to use Newtonsoft.Json. Why did I choose this? It was an easy decision: Newtonsoft.Json comes with Sitecore “out of the box” so it was convenient for me to choose this as a way to parse the JSON coming from the Giphy API.
I created the following model classes with JSON to C# property mappings:
using Newtonsoft.Json; namespace Sitecore.Sandbox.Providers { public class GiphyData { [JsonProperty("type")] public string Type { get; set; } [JsonProperty("id")] public string Id { get; set; } [JsonProperty("url")] public string Url { get; set; } [JsonProperty("image_original_url")] public string ImageOriginalUrl { get; set; } [JsonProperty("image_url")] public string ImageUrl { get; set; } [JsonProperty("image_mp4_url")] public string ImageMp4Url { get; set; } [JsonProperty("image_frames")] public string ImageFrames { get; set; } [JsonProperty("image_width")] public string ImageWidth { get; set; } [JsonProperty("image_height")] public string ImageHeight { get; set; } [JsonProperty("fixed_height_downsampled_url")] public string FixedHeightDownsampledUrl { get; set; } [JsonProperty("fixed_height_downsampled_width")] public string FixedHeightDownsampledWidth { get; set; } [JsonProperty("fixed_height_downsampled_height")] public string FixedHeightDownsampledHeight { get; set; } [JsonProperty("fixed_width_downsampled_url")] public string FixedWidthDownsampledUrl { get; set; } [JsonProperty("fixed_width_downsampled_width")] public string FixedWidthDownsampledWidth { get; set; } [JsonProperty("fixed_width_downsampled_height")] public string FixedWidthDownsampledHeight { get; set; } [JsonProperty("fixed_height_small_url")] public string FixedHeightSmallUrl { get; set; } [JsonProperty("fixed_height_small_still_url")] public string FixedHeightSmallStillUrl { get; set; } [JsonProperty("fixed_height_small_width")] public string FixedHeightSmallWidth { get; set; } [JsonProperty("fixed_height_small_height")] public string FixedHeightSmallHeight { get; set; } [JsonProperty("fixed_width_small_url")] public string FixedWidthSmallUrl { get; set; } [JsonProperty("fixed_width_small_still_url")] public string FixedWidthSmallStillUrl { get; set; } [JsonProperty("fixed_width_small_width")] public string FixedWidthSmallWidth { get; set; } [JsonProperty("fixed_width_small_height")] public string FixedWidthSmallHeight { get; set; } [JsonProperty("username")] public string Username { get; set; } [JsonProperty("caption")] public string Caption { get; set; } } }
using Newtonsoft.Json; namespace Sitecore.Sandbox.Providers { public class GiphyMeta { [JsonProperty("status")] public int Status { get; set; } [JsonProperty("msg")] public string Message { get; set; } } }
using Newtonsoft.Json; namespace Sitecore.Sandbox.Providers { public class GiphyResponse { [JsonProperty("data")] public GiphyData Data { get; set; } [JsonProperty("meta")] public GiphyMeta Meta { get; set; } } }
Every property above in every class represents a JSON property/object in the response coming back from the Giphy API.
Now, we need a way to make a request to the Giphy API. I built the following interface whose instances will do just that:
namespace Sitecore.Sandbox.Providers { public interface IGiphyImageProvider { GiphyData GetRandomGigphyImageData(string tags); } }
The following class implements the interface above:
using System; using System.Net; using Sitecore.Diagnostics; using Newtonsoft.Json; using System.IO; namespace Sitecore.Sandbox.Providers { public class GiphyImageProvider : IGiphyImageProvider { private string RequestUrlFormat { get; set; } private string ApiKey { get; set; } public GiphyData GetRandomGigphyImageData(string tags) { Assert.IsNotNullOrEmpty(RequestUrlFormat, "RequestUrlFormat"); Assert.IsNotNullOrEmpty(ApiKey, "ApiKey"); Assert.ArgumentNotNullOrEmpty(tags, "tags"); string response = GetJsonResponse(GetRequestUrl(tags)); if(string.IsNullOrWhiteSpace(response)) { return new GiphyData(); } try { GiphyResponse giphyResponse = JsonConvert.DeserializeObject<GiphyResponse>(response); if(giphyResponse != null && giphyResponse.Meta != null && giphyResponse.Meta.Status == 200 && giphyResponse.Data != null) { return giphyResponse.Data; } } catch(Exception ex) { Log.Error(ToString(), ex, this); } return new GiphyData(); } protected virtual string GetRequestUrl(string tags) { Assert.ArgumentNotNullOrEmpty(tags, "tags"); return string.Format(RequestUrlFormat, ApiKey, Uri.EscapeDataString(tags)); } protected virtual string GetJsonResponse(string requestUrl) { Assert.ArgumentNotNullOrEmpty(requestUrl, "requestUrl"); try { WebRequest request = HttpWebRequest.Create(requestUrl); request.Method = "GET"; string json; using (WebResponse response = request.GetResponse()) { using (Stream responseStream = response.GetResponseStream()) { using (StreamReader sr = new StreamReader(responseStream)) { return sr.ReadToEnd(); } } } } catch (Exception ex) { Log.Error(ToString(), ex, this); } return string.Empty; } } }
Code in the methods above basically take in tags for the type of random image we want from Giphy; build up the request URL — the template of the request URL and API key (I’m using the public key which is open for developers to experiment with) are populated via the Sitecore Configuration Factory (have a look at the patch include configuration file further down in this post to get an idea of how the properties of this class are populated); make the request to the Giphy API; get back the response; hand the response over to some Newtonsoft.Json API code to parse JSON into model instances of the classes shown further above in this post; and then return the nested model instances.
I then created the following Sitecore.Shell.Applications.ContentEditor.Image subclass which represents the custom Content Editor Image field:
using System; using Sitecore.Configuration; using Sitecore.Data.Items; using Sitecore.Diagnostics; using Sitecore.Pipelines; using Sitecore.Shell.Framework; using Sitecore.Web.UI.Sheer; using Sitecore.Sandbox.Pipelines.DownloadImageToMediaLibrary; using Sitecore.Sandbox.Providers; namespace Sitecore.Sandbox.Shell.Applications.ContentEditor { public class GiphyImage : Sitecore.Shell.Applications.ContentEditor.Image { private IGiphyImageProvider GiphyImageProvider { get; set; } public GiphyImage() : base() { GiphyImageProvider = GetGiphyImageProvider(); } protected virtual IGiphyImageProvider GetGiphyImageProvider() { IGiphyImageProvider giphyImageProvider = Factory.CreateObject("imageProviders/giphyImageProvider", false) as IGiphyImageProvider; Assert.IsNotNull(giphyImageProvider, "The giphyImageProvider was not properly defined in configuration"); return giphyImageProvider; } public override void HandleMessage(Message message) { Assert.ArgumentNotNull(message, "message"); if (string.Equals(message.Name, "contentimage:downloadGiphy", StringComparison.CurrentCultureIgnoreCase)) { GetInputFromUser(); return; } base.HandleMessage(message); } protected void GetInputFromUser() { RunProcessor("GetGiphyTags", new ClientPipelineArgs()); } protected virtual void GetGiphyTags(ClientPipelineArgs args) { if (!args.IsPostBack) { SheerResponse.Input("Enter giphy tags:", string.Empty); args.WaitForPostBack(); } else if (args.HasResult) { args.Parameters["tags"] = args.Result; args.IsPostBack = false; RunProcessor("GetGiphyImageUrl", args); } else { CancelOperation(args); } } protected virtual void GetGiphyImageUrl(ClientPipelineArgs args) { GiphyData giphyData = GiphyImageProvider.GetRandomGigphyImageData(args.Parameters["tags"]); if (giphyData == null || string.IsNullOrWhiteSpace(giphyData.ImageUrl)) { SheerResponse.Alert("Unfortunately, no image matched the tags you specified. Please try again."); CancelOperation(args); return; } args.Parameters["imageUrl"] = giphyData.ImageUrl; args.IsPostBack = false; RunProcessor("ChooseMediaLibraryFolder", args); } protected virtual void RunProcessor(string processor, ClientPipelineArgs args) { Assert.ArgumentNotNullOrEmpty(processor, "processor"); Sitecore.Context.ClientPage.Start(this, processor, args); } public void ChooseMediaLibraryFolder(ClientPipelineArgs args) { if (!args.IsPostBack) { Dialogs.BrowseItem ( "Select A Media Library Folder", "Please select a media library folder to store the Giphy image.", "Applications/32x32/folder_into.png", "OK", "/sitecore/media library", string.Empty ); args.WaitForPostBack(); } else if (args.HasResult) { Item folder = Client.ContentDatabase.Items[args.Result]; args.Parameters["mediaLibaryFolderPath"] = folder.Paths.FullPath; args.IsPostBack = false; RunProcessor("DownloadImage", args); } else { CancelOperation(args); } } protected virtual void DownloadImage(ClientPipelineArgs args) { DownloadImageToMediaLibraryArgs downloadArgs = new DownloadImageToMediaLibraryArgs { Database = Client.ContentDatabase, ImageUrl = args.Parameters["imageUrl"], MediaLibaryFolderPath = args.Parameters["mediaLibaryFolderPath"] }; CorePipeline.Run("downloadImageToMediaLibrary", downloadArgs); SetMediaItemInField(downloadArgs); } protected virtual void SetMediaItemInField(DownloadImageToMediaLibraryArgs args) { Assert.ArgumentNotNull(args, "args"); if(string.IsNullOrWhiteSpace(args.MediaId) || string.IsNullOrWhiteSpace(args.MediaPath)) { return; } XmlValue.SetAttribute("mediaid", args.MediaId); Value = args.MediaPath; Update(); SetModified(); } protected virtual void CancelOperation(ClientPipelineArgs args) { Assert.ArgumentNotNull(args, "args"); args.AbortPipeline(); } } }
The class above does not differ much from the Image class I shared in my previous post. The only differences are in the instantiation of an IGiphyImageProvider object using the Sitecore Configuration Factory — this object is used for getting the Giphy image URL from the Giphy API; the GetGiphyTags() method prompts the user for tags used in calling up a random image from Giphy; and in the GetGiphyImageUrl() method which uses the IGiphyImageProvider instance to get the image URL. The rest of the code in this class is unmodified from the Image class shared in my previous post.
I then defined the IGiphyImageProvider code in the following patch include configuration file:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <imageProviders> <giphyImageProvider type="Sitecore.Sandbox.Providers.GiphyImageProvider, Sitecore.Sandbox" singleInstance="true"> <RequestUrlFormat>http://api.giphy.com/v1/gifs/random?api_key={0}&tag={1}</RequestUrlFormat> <ApiKey>dc6zaTOxFJmzC</ApiKey> </giphyImageProvider> </imageProviders> </sitecore> </configuration>
Be sure to check out the patch include configuration file from my previous post as it contains the custom pipeline that downloads images from a URL.
You should also refer my previous post which shows you how to register a custom Content Editor field in the core database of Sitecore.
Let’s test this out.
We need to add this new field to a template. I’ve added it to the “out of the box” Sample Item template:
My Home item uses the above template. Let’s download a random Giphy image on it:
I then supplied some tags for getting a random image:
Let’s choose a place to save the image in the Media Library:
As you can see, the image was downloaded and saved into the Media Library in the selected folder, and then saved in the custom field on the Home item:
If you are curious, this is the image that was returned by the Giphy API:
If you have any thoughts on this, please share in a comment.
[…] Download Random Giphy Images and Save to the Media Library Via a Custom Content Editor Image Field i… […]