/// <summary>
        /// Get all the content channel items that should be displayed.
        /// </summary>
        /// <param name="contentChannel">The content channel whose items are to be displayed.</param>
        /// <param name="rockContext">The context to load any extra data from.</param>
        /// <returns>A SignContents object that contains the formatted data for the client.</returns>
        private SignContents GetContentChannelItems(List <int> campusIds, ContentChannel contentChannel, RockContext rockContext, List <int> visitedContentChannels = null)
        {
            var contents = new SignContents();
            var items    = contentChannel.Items.Where(i => i.StartDateTime <= DateTime.Now && (i.ExpireDateTime == null || i.ExpireDateTime > DateTime.Now));

            //
            // Add ourselves to the visited content channels so we don't recurse forever.
            //
            visitedContentChannels = visitedContentChannels ?? new List <int>();
            visitedContentChannels.Add(contentChannel.Id);

            //
            // Get any configuration options from the content channel.
            //
            contentChannel.LoadAttributes(rockContext);
            if (contentChannel.GetAttributeValue("com_shepherdchurch_SlideInterval").AsInteger() >= 4)
            {
                contents.SlideInterval = contentChannel.GetAttributeValue("com_shepherdchurch_SlideInterval").AsInteger() * 1000;
            }
            if (!string.IsNullOrWhiteSpace(contentChannel.GetAttributeValue("com_shepherdchurch_Transitions")))
            {
                contents.Transitions = contentChannel.GetAttributeValues("com_shepherdchurch_Transitions").ToArray();
            }

            //
            // Order the items either manually or by start date time, depending on configuration.
            //
            if (contentChannel.ItemsManuallyOrdered)
            {
                items = items.OrderBy(i => i.Order);
            }
            else
            {
                items = items.OrderBy(i => i.StartDateTime);
            }

            //
            // Loop each content item and see if it contains something to display.
            //
            foreach (var item in items)
            {
                if (item.Attributes == null)
                {
                    item.LoadAttributes(rockContext);
                }

                //
                // Check for valid campus.
                //
                var campusFilter = item.GetAttributeValues("com_shepherdchurch_CampusFilter")
                                   .AsGuidOrNullList()
                                   .Where(g => g.HasValue)
                                   .Select(g => CampusCache.Get(g.Value))
                                   .Where(c => c != null)
                                   .Select(c => c.Id)
                                   .ToList();

                if (campusFilter.Count > 0 && campusIds.Count > 0 && campusFilter.Intersect(campusIds).Count() == 0)
                {
                    continue;
                }

                var duration = item.GetAttributeValue("com_shepherdchurch_Duration").AsIntegerOrNull();

                //
                // Check which kind of slide to include.
                //
                if (!string.IsNullOrWhiteSpace(item.GetAttributeValue("com_shepherdchurch_IncludeContentFrom")))
                {
                    //
                    // Process the "Include Content From" attribute.
                    //
                    var guid = item.GetAttributeValue("com_shepherdchurch_IncludeContentFrom").AsGuid();
                    var childContentChannel = new ContentChannelService(rockContext).Get(guid);

                    if (childContentChannel != null && !visitedContentChannels.Contains(childContentChannel.Id))
                    {
                        var childContents = GetContentChannelItems(campusIds, childContentChannel, rockContext, visitedContentChannels);

                        contents.Audio.AddRange(childContents.Audio);
                        contents.Slides.AddRange(childContents.Slides);
                    }
                }
                else if (!string.IsNullOrWhiteSpace(item.GetAttributeValue("com_shepherdchurch_SlideUrl")))
                {
                    //
                    // Process the "Slide Url" attribute.
                    //
                    var fileUrl = item.GetAttributeValue("com_shepherdchurch_SlideUrl");
                    if (Regex.IsMatch(fileUrl, "\\.mp3(\\?|$)", RegexOptions.IgnoreCase))
                    {
                        contents.Audio.Add(fileUrl);
                    }
                    else
                    {
                        contents.Slides.Add(new Slide(fileUrl, duration));
                    }
                }
                else
                {
                    //
                    // Process the "Slide" attribute.
                    // Check if it contains either an audio file or an image.
                    //
                    var guid       = item.GetAttributeValue("com_shepherdchurch_Slide").AsGuid();
                    var binaryFile = new BinaryFileService(rockContext).Get(guid);

                    if (binaryFile != null && binaryFile.Id != 0)
                    {
                        if (binaryFile.MimeType.StartsWith("audio/"))
                        {
                            contents.Audio.Add(VirtualPathUtility.ToAbsolute(string.Format("~/GetFile.ashx?id={0}", binaryFile.Id)));
                        }
                        else if (binaryFile.MimeType.StartsWith("image/"))
                        {
                            contents.Slides.Add(new Slide(VirtualPathUtility.ToAbsolute(string.Format("~/GetImage.ashx?id={0}", binaryFile.Id)), duration));
                        }
                    }
                }
            }

            return(contents);
        }