Monthly Archives: January 2013

Windows 8 Active Tiles not appearing when setting image as URL with Windows Authentication

I’d created Active Tiles using a Background task exactly as other tutorials said to and when using a text only active tile, the process worked fine. But whenever I specified an Image using a web URL the tiles simply did not appear. This had me stumped for ages and nothing useful was appearing in the event log other than “The message id for the desired message could not be found”. Useless!

After much scratching of heads I worked out that the Tile image URL does not work if the image sits on a web site which uses any kind of authentication mechanism such as Windows or Claims based. i.e. applications such as MS SharePoint or MS Dynamics CRM. The solution I cam up with was to download the image locally in the background task where you can specify client credentials and then reference it from the application temporary storage in the Tile.

Here is the code I used below. The Background task calls an async method to populate the tiles based on the results of an RSS feed.


using MyCompany.Windows8.App.MyCompanyNewsReaderHelper.DataModel;
using MyCompany.Windows8.App.MyCompanyNewsReaderHelper.Model;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Windows.ApplicationModel.Background;
using Windows.Data.Xml.Dom;
using Windows.Storage;
using Windows.UI.Notifications;

namespace MyCompany.Windows8.App.MyCompanyNewsBackgroundTasks
{
    public sealed class MyCompanyNewsTileUpdater : IBackgroundTask
    {

        private List<FeedItem> _feedItems;
        private volatile object _lockFeedItemsList = new object();
        private IBackgroundTaskInstance taskInstance;
        private BackgroundTaskDeferral defferal;

        public void Run(IBackgroundTaskInstance taskInstance)
        {
            defferal = taskInstance.GetDeferral();
            try
            {
                RegisterDataSourceEvents();
                this.taskInstance = taskInstance;
                Debug.WriteLine("Trying to update MyCompanyNews Tile");
                FeedsDataSource.Instance.LoadFeedsParallel(true, false);

            }
            catch (Exception ex)
            {
                Debug.WriteLine("RSS Exception : " + ex.ToString());
            }
           
        }

        private void RegisterDataSourceEvents()
        {
            FeedsDataSource.Instance.FeedItemsAdded += OnFeedItemsAdded;
            FeedsDataSource.Instance.FeedItemsCleared += OnFeedItemsCleared;
            FeedsDataSource.Instance.FeedItemsLoadingCompleted += OnFeedItemsLoadingCompleted;
        }

        private async void OnFeedItemsLoadingCompleted(object sender, FeedItemsLoadingCompletedEventArgs feedItemsLoadingCompletedEventArgs)
        {
            Debug.WriteLine("Rss feed loading complete");
            _feedItems = FeedsDataSource.Instance.FeedItems;

           Debug.WriteLine(String.Format("Loaded {0} RSS items",_feedItems.Count.ToString()));
           
           var updater = TileUpdateManager.CreateTileUpdaterForApplication();
           updater.EnableNotificationQueue(true);
           updater.Clear();

           int i = 0;
           foreach (FeedItem item in _feedItems)
           {
               Debug.WriteLine("trying to load tile for post " + item.Title);
               if (!String.IsNullOrEmpty(item.mainImageUrl))
               {
                   // Unfortunately it seems that when the tile displays the image from a direct URL
                   // Windows authentication is supported. But it is here so we will download the image to application
                   Uri imgLocation = await GetLocalImageAsync(item.mainImageUrl, item.UniqueId);
                   var tileWide = TileUpdateManager.GetTemplateContent(TileTemplateType.TileWideSmallImageAndText03);
                   var tileWideVisual = tileWide.GetElementsByTagName("visual");
                   ((XmlElement)tileWideVisual[0]).SetAttribute("branding", "logo");

                   var textNodes = tileWide.GetElementsByTagName("text");
                   textNodes[0].AppendChild(tileWide.CreateTextNode(item.Title));
                   var tileImageAttributes = tileWide.GetElementsByTagName("image");
                   Debug.WriteLine("image URL is " + item.thumbnailImageUrl);
                   ((XmlElement)tileImageAttributes[0]).SetAttribute("src", imgLocation.AbsoluteUri);
                   ((XmlElement)tileImageAttributes[0]).SetAttribute("alt", "red graphic");


                   Debug.WriteLine("XML is " + tileWide.GetXml());
                   updater.Update(new TileNotification(tileWide));

                   i++;
               }

               // We can only store 4 tiles at a time
               if (i > 4)
                   break;
               
           }

           Debug.WriteLine("Finished updating tiles");
           defferal.Complete();
        }

        private void OnFeedItemsAdded(object sender, FeedItemsAddedEventArgs e)
        {
            Debug.WriteLine("Feed item added");
            _feedItems = FeedsDataSource.Instance.FeedItems;
        }

        private void OnFeedItemsCleared(object sender, FeedItemsClearedEventArgs feedItemsClearedEventArgs)
        {
            Debug.WriteLine("Feed items cleared");
                _feedItems = null;
        }

        private async Task<Uri> GetLocalImageAsync(string internetUri, string uniqueName)
        {
            if (string.IsNullOrEmpty(internetUri))
            {
                return null;
            }

            HttpWebRequest theRequest = HttpWebRequest.CreateHttp(internetUri);
            theRequest.UseDefaultCredentials = true;

            using (var response = await theRequest.GetResponseAsync())
            {
                using (var stream = response.GetResponseStream())
                {
                    var desiredName = string.Format("{0}.jpg", uniqueName);
                    var file = await ApplicationData.Current.LocalFolder.CreateFileAsync(desiredName, CreationCollisionOption.ReplaceExisting);
                    

                    
                    List<Byte> allBytes = new List<byte>();
                    using (Stream imageStream = response.GetResponseStream())
                    {
                        byte[] buffer = new byte[4000];
                        int bytesRead = 0;
                        while ((bytesRead = await imageStream.ReadAsync(buffer, 0, 4000)) > 0)
                        {
                            allBytes.AddRange(buffer.Take(bytesRead));
                        }
                    }
                    
                    await FileIO.WriteBytesAsync(file, allBytes.ToArray()); 

                    
                    return new Uri(string.Format("ms-appdata:///local/{0}.jpg", uniqueName), UriKind.Absolute);
                    
                }
            }
        }
    }
}


 

Creating an RSS Feed from a SharePoint 2010 Web Part

I’ve got a simple SharePoint web part which displays news items based on an external data source and the requirement is to build an RSS2 feed which also included some custom image tags similar to the Channel 9 RSS feed. The out the box RSS feeds aren’t valid as we are dealing with a custom data source, but even if they were, they wouldn’t have the custom tags included. So here is what I did.

All the existing code for the web part I left as is, but I added an if statement to check for GET parameters in the URL. So if the URL looks like http://mysharepointsite.domain.com/Team/MyNews.aspx?feed=rss2 then the RSS feed is displayed, otherwise it’s just the same web part as it was before. This all happens under the protected void Page_Load(object sender, EventArgs e) method in the web part.

if (Request.QueryString["feed"] != null && Request.QueryString["feed"] == "rss2")

Then I go ahead and write the feed. First we clear out anything the page has already generated. Response.Clear();

Next we construct our XML document conforming to RSS standards. Note that I am using the “media” namespace used by the channel 9 RSS feed which give me options for using image/thumbnail tags.

Response.ContentType = "text/xml";
XmlTextWriter feedWriter
	= new XmlTextWriter(Response.OutputStream, Encoding.UTF8);

feedWriter.WriteStartDocument();

// These are RSS Tags
feedWriter.WriteStartElement("rss");
feedWriter.WriteAttributeString("version", "2.0");
feedWriter.WriteAttributeString("xmlns", "media", null, "http://search.yahoo.com/mrss/");

feedWriter.WriteStartElement("channel");
feedWriter.WriteElementString("title", "News");
feedWriter.WriteElementString("link", PANewsUrl);
feedWriter.WriteElementString("description", "News RSS Feed");
feedWriter.WriteElementString("copyright",
	String.Format("Copyright {0} My Company. All rights reserved.",DateTime.Now.Year));

Then Trigger your business logic to get the articles from wherever your data source, e.g. a database or WCF service. This bit will change depending on the service you are writing. In my example I’m returning a list of “Articles” from a web service.

Articles = Article.GetArticles(strSection, 20, Mode, "MainSection");

Then I loop through the list of Articles and add the nodes to my XMLWriter object. Note the custom image tags used which enable thumbnails and main images to be displayed in the feed. This is very useful for visually rich clients like mobile apps.

foreach (Article post in Articles)
{
	feedWriter.WriteStartElement("item");
	feedWriter.WriteElementString("title", post.Header);
	feedWriter.WriteElementString("description", post.Content + post.ContentDetail);
	feedWriter.WriteElementString("link", post.ArticleUrl);
	feedWriter.WriteElementString("pubDate",
		post.PublishedDate.ToString());
	if (!String.IsNullOrEmpty(post.ImageThumbnailUrl))
	{
		feedWriter.WriteStartElement("media:thumbnail");
		feedWriter.WriteAttributeString("url", post.ImageThumbnailUrl);
		feedWriter.WriteEndElement();
	}
	if (!String.IsNullOrEmpty(post.ImageUrl))
	{
		feedWriter.WriteStartElement("media:content");
		feedWriter.WriteAttributeString("url", post.ImageUrl);
		feedWriter.WriteAttributeString("medium", "image");
		feedWriter.WriteElementString("media:title",
			post.ImageTitle);
		feedWriter.WriteEndElement();
	}
	feedWriter.WriteEndElement();
}

Finally I close all the tags and end my response.

// Close all open tags tags
feedWriter.WriteEndElement();
feedWriter.WriteEndElement();
feedWriter.WriteEndDocument();
feedWriter.Flush();
feedWriter.Close();
Response.End();

Now when I deploy and navigate to any SharePoint page containing my web part and append the GET parameters ?feed=rss2, an RSS feed is displayed instead of the normal content.