About the author

Bart X. Tubalinal

Bart X. Tubalinal is a Sr. Consultant for a company based in the Chicagoland area. His primary focus is on the Microsoft technology stack, including: .NET, C#, SharePoint, InfoPath, Silverlight, Windows Presentation Foundation (WPF), Windows Workflow Foundation (WF), and ASP.NET. He has also dabbled a little in Project Server.

Though he doesn't have as much free time as he would like, when he does, he loves to hang out with friends, try different cuisines, and play with his dog, Tyson.

He also considers himself to be, pound for pound, one of the best developers there is.

Facebook ProfileTwitter: bart_tubalinal

RecentComments

Comment RSS

 

FAST ESP Powershell cmdlet for deleting multiple files from the index

by Bart X. Tubalinal 21. December 2010 15:11
I posted a new blog on my company blog site. It is about writing a Powershell cmdlet along with the FAST ESP Content API to allow you to quickly delete multiple items out of the FAST index. Check it out here: http://blogs.pointbridge.com/Blogs/tubalinal_bart/Pages/Post.aspx?_ID=8
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Currently rated 3.1 by 16 people

  • Currently 3.125/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

FAST Search | PowerShell

Querying an SPFieldMultiLineText field with LINQ

by Bart X. Tubalinal 1. October 2010 16:09
I just posted another blog entry on my work blog. It covers a technique on how you can use LINQ to query a SharePoint multiline text field. Check it out here: http://blogs.pointbridge.com/Blogs/tubalinal_bart/pages/default.aspx
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Currently rated 3.0 by 5 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

LINQ | SharePoint | SharePoint 2010

Building a Federated Search Experience using FAST Search for Internet Sites (FSIS)

by Bart X. Tubalinal 11. September 2010 11:58

I just posted a blog on my company site about an example federated search application built with FAST Search for Internet Sites (FSIS) and its Interaction Management Services (IMS) engine. You can find that blog post here: http://blogs.pointbridge.com/Blogs/tubalinal_bart/Pages/Post.aspx?_ID=5.

On another note, I will be continue to write blog posts on this site but I will also post on my company site. Whenever I post on my company site, I will also include the link here. 

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Currently rated 3.0 by 5 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

FAST Search

Tip of the Day: Export Outlook 2010 Contacts

by Bart X. Tubalinal 23. June 2010 16:44

So I'm leaving the company I'm currently at and joining a new one next week. I have personal contacts in my address book but it took me forever trying to figure out how to export my contacts from Outlook 2010. Let's just say the location is counter-intuitive. Anyways, to export your contacts, here are the steps: 

  1. Click on the File tab.
  2. Select Open from the menu (Part 1 of the counter-intuitiveness).
  3. Select the Import option (Part 2 of the counter-intuitiveness).
  4. Select Export to a file.
  5. Click the Next button.
  6. Select the appropriate file type (comma or tab separated, Outlook data file, etc).
  7. Select your Contacts folder.
  8. Click the Browse button and navigate to the folder you want to save the file to and give the file a file name.
  9. Click the Ok button.
  10. Ensure the appropriate action is displayed and selected in the actions list and click the Finish button.
That's it. I know, not the most helpful or thought-provoking blog post I've ever written but I hope this can help some poor sap like me who doesn't think quite like the Microsoft UX guys do and automatically assume that the most appropriate place for an Export action would be under Open | Import ;)

 

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Currently rated 3.0 by 5 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

Office 2010

SharePoint 2010 Download as Zip File Custom Ribbon Action

by Bart X. Tubalinal 8. May 2010 12:37

So I was bored and decided I wanted to learn a little more about the new SharePoint 2010 UI Framework. There are some really cool things you can do with the new framework and I wanted to put some of them to use but at the same time create something that’s useful for other people. I remember a while back someone asking me to write code for MOSS 2007 that would allow users to download files down to their computer as a zip file because they didn’t like having to download each file one by one. For some reason or another, I never got around to it – I think I told the person that they could download multiple files from SharePoint using Windows Explorer and, although it wasn’t exactly what they were looking for, it at least got the job done. But I decided recently that this might not be a bad enhancement to add to the UI in 2010 so that’s what I set out to build today.

The first thing to look at is my Visual Studio 2010 project. I will discuss all the important files that are part of this project. Below is a screenshot of my project:

SolutionExplorer

The first thing I did was to create a new SharePoint 2010 Empty SharePoint Project called DeviantPoint.DownloadZip. Then, I added a reference to ICSharpCode.SharpZipLib.dll which is a part of SharpZipLib(a .NET library that is well-used for working with Zip files). I then created a helper class, ZipBuilder, used to actually create the zip file. I created this helper class with the intent that I could reuse this elsewhere, not just for this project.

Basically, when an instance of this class is constructed, you need to pass in a stream that will be used to write the contents of the zip file to. This could be any type stream (FileStream, MemoryStream, etc). There are a couple of helper methods in this class that allow you to add files and folders and method that “finalizes” the zip file. This Finalize() method must always be called to ensure that the zip file is written out correctly. This class also implements the IDisposable pattern since it is handling streams.

This is the code for the ZipBuilder class:

ZipBuilder.cs

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.IO;
   6: using ICSharpCode.SharpZipLib.Zip;
   7: using ICSharpCode.SharpZipLib.Core;
   8:  
   9: namespace DeviantPoint.DownloadZip
  10: {
  11:     public class ZipFileBuilder : IDisposable
  12:     {
  13:         private bool disposed = false;
  14:  
  15:         ZipOutputStream zipStream = null;
  16:         protected ZipOutputStream ZipStream
  17:         {
  18:             get { return zipStream; }
  19:  
  20:         }
  21:  
  22:         ZipEntryFactory factory = null;
  23:         private ZipEntryFactory Factory
  24:         {
  25:             get { return factory; }
  26:         }
  27:  
  28:  
  29:         public ZipFileBuilder(Stream outStream)
  30:         {
  31:             zipStream = new ZipOutputStream(outStream);
  32:             zipStream.SetLevel(9); //best compression
  33:  
  34:             factory = new ZipEntryFactory(DateTime.Now);
  35:         }
  36:  
  37:         public void Add(string fileName, Stream fileStream)
  38:         {
  39:             //create a new zip entry            
  40:             ZipEntry entry = factory.MakeFileEntry(fileName);
  41:             entry.DateTime = DateTime.Now;
  42:             ZipStream.PutNextEntry(entry);
  43:  
  44:             byte[] buffer = new byte[65536];
  45:  
  46:             int sourceBytes;
  47:             do
  48:             {
  49:                 sourceBytes = fileStream.Read(buffer, 0, buffer.Length);
  50:                 ZipStream.Write(buffer, 0, sourceBytes);
  51:             }
  52:             while (sourceBytes > 0);
  53:  
  54:  
  55:         }
  56:  
  57:         public void AddDirectory(string directoryName)
  58:         {
  59:             ZipEntry entry = factory.MakeDirectoryEntry(directoryName);
  60:             ZipStream.PutNextEntry(entry);
  61:         }
  62:  
  63:         public void Finish()
  64:         {
  65:             if (!ZipStream.IsFinished)
  66:             {
  67:                 ZipStream.Finish();
  68:             }
  69:         }
  70:  
  71:         public void Close()
  72:         {
  73:             Dispose(true);
  74:             GC.SuppressFinalize(this);
  75:         }
  76:  
  77:         public void Dispose()
  78:         {
  79:             this.Close();
  80:         }
  81:  
  82:         protected virtual void Dispose(bool disposing)
  83:         {
  84:             if (!disposed)
  85:             {
  86:                 if (disposing)
  87:                 {
  88:                     if (ZipStream != null)
  89:                         ZipStream.Dispose();
  90:                 }
  91:             }
  92:  
  93:             disposed = true;
  94:         }
  95:     }
  96: }

The next thing I wrote was SPExtensions.cs, a class for adding extension methods to some of the Microsoft.SharePoint objects. This class basically just adds a few simple helper methods to the SPListItem class and the SPList class. For the SPListItem class, I just added a method to determine if the SPListItem instance is actually a folder and for the SPList class, I added a method to determine if the list is actually a document library.

The code for SPExtensions is below:

SPExtensions.cs

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.Runtime.CompilerServices;
   6: using Microsoft.SharePoint;
   7:  
   8: namespace DeviantPoint.DownloadZip
   9: {
  10:     public static class SPExtensions
  11:     {
  12:         public static bool IsFolder(this SPListItem item)
  13:         {
  14:             return (item.Folder != null);
  15:         }
  16:  
  17:         public static bool IsDocumentLibrary(this SPList list)
  18:         {
  19:             return (list.BaseType == SPBaseType.DocumentLibrary);
  20:         }
  21:     }
  22: }

The next thing I did was to add a SharePoint Mapped Folder to my project mapping to the Layouts directory located in the SharePoint root. When you add a mapped folder, Visual Studio will automatically create a sub-folder in that mapped folder with the same name as your project. This is a good thing as you don’t want to be mixing up all your project files with all of the files that come out of the box from SharePoint.

After I had my Layouts mapped folder and subfolder created, I added a SharePoint 2010 Application Page item to the sub-folder called DownloadZip.aspx. The purpose of this application page is to actually handle the request from the client to build the zip file and send it back down to the client. Having an application page handle this is the same technique that is used with the ‘Download a Copy’ action button you see in the SharePoint 2010 ribbon. Basically, a POST request from a client is sent to my DownloadZip.aspx page and this page takes care of packaging up a zip file and sending it down to the client’s browser. This page expects two parameters:

  • sourceUrl –the full url of the document library (and folder, if inside of a subfolder) where the request is being made from
  • itemIDs – a semi-colon delimited list of the SPListItem IDs that should be included as part of the zip file. Note that folders also have ids so if a folder is selected, that folder’s id would also be sent.

The code-behind for this application page basically takes the list of item ids and for each item id, goes and grabs the corresponding file from the document library in SharePoint and, using the ZipBuilder class, packages it up as a zip file. If one of the items that was selected is actually a folder, it will create that folder in the zip file as well and put all the items that are in that SharePoint folder into the corresponding zip file folder. It will also traverse through all the sub-folders in the hierarchy.

Below is the code-behind for the DownloadZip.aspx application page (there is nothing I added to the Download.aspx file itself):

DownloadZip.aspx.cs

   1: using System;
   2: using System.IO;
   3: using System.Web;
   4: using Microsoft.SharePoint;
   5: using Microsoft.SharePoint.WebControls;
   6:  
   7: namespace DeviantPoint.DownloadZip.Layouts.DeviantPoint.DownloadZip
   8: {
   9:     public partial class DownloadZip : LayoutsPageBase
  10:     {
  11:         protected void Page_Load(object sender, EventArgs e)
  12:         {            
  13:             string fullDocLibSourceUrl = Request.Params["sourceUrl"];
  14:             if (string.IsNullOrEmpty(fullDocLibSourceUrl)) return;
  15:  
  16:             string docLibUrl = fullDocLibSourceUrl.Replace(SPContext.Current.Site.Url, "");
  17:  
  18:             SPList list = SPContext.Current.Web.GetList(docLibUrl);
  19:             if (!list.IsDocumentLibrary()) return;
  20:  
  21:             string pItemIds = Request.Params["itemIDs"];
  22:             if (string.IsNullOrEmpty(pItemIds)) return;
  23:  
  24:             SPDocumentLibrary library = (SPDocumentLibrary)list;
  25:  
  26:             string[] sItemIds = pItemIds.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
  27:             int[] itemsIDs = new int[sItemIds.Length];
  28:             for (int i = 0; i < sItemIds.Length; i++)
  29:             {
  30:                 itemsIDs[i] = Convert.ToInt32(sItemIds[i]);
  31:             }
  32:  
  33:             if (itemsIDs.Length > 0)
  34:             {
  35:                 using (MemoryStream ms = new MemoryStream())
  36:                 {
  37:                     using (ZipFileBuilder builder = new ZipFileBuilder(ms))
  38:                     {
  39:                         foreach (int id in itemsIDs)
  40:                         {
  41:                             SPListItem item = library.GetItemById(id);
  42:                             if (item.IsFolder())
  43:                                 AddFolder(builder, item.Folder, string.Empty);
  44:                             else
  45:                                 AddFile(builder, item.File, string.Empty);
  46:                         }
  47:  
  48:                         builder.Finish();
  49:                         WriteStreamToResponse(ms);
  50:                     }
  51:                 }
  52:             }
  53:  
  54:         }
  55:  
  56:         private static void AddFile(ZipFileBuilder builder, SPFile file, string folder)
  57:         {
  58:             using (Stream fileStream = file.OpenBinaryStream())
  59:             {
  60:                 builder.Add(folder + "\\" + file.Name, fileStream);
  61:                 fileStream.Close();
  62:             }
  63:         }
  64:  
  65:         private void AddFolder(ZipFileBuilder builder, SPFolder folder, string parentFolder)
  66:         {
  67:             string folderPath = parentFolder == string.Empty ? folder.Name : parentFolder + "\\" +folder.Name;
  68:             builder.AddDirectory(folderPath);
  69:  
  70:             foreach (SPFile file in folder.Files)
  71:             {
  72:                 AddFile(builder, file, folderPath);
  73:             }
  74:  
  75:             foreach (SPFolder subFolder in folder.SubFolders)
  76:             {
  77:                 AddFolder(builder, subFolder, folderPath);
  78:             }
  79:         }
  80:  
  81:         private void WriteStreamToResponse(MemoryStream ms)
  82:         {
  83:             if (ms.Length > 0)
  84:             {
  85:                 string filename = DateTime.Now.ToFileTime().ToString() + ".zip";
  86:                 Response.Clear();
  87:                 Response.ClearHeaders();
  88:                 Response.ClearContent();
  89:                 Response.AddHeader("Content-Length", ms.Length.ToString());
  90:                 Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
  91:                 Response.ContentType = "application/octet-stream";
  92:  
  93:                 byte[] buffer = new byte[65536];
  94:                 ms.Position = 0;
  95:                 int num;
  96:                 do
  97:                 {
  98:                     num = ms.Read(buffer, 0, buffer.Length);
  99:                     Response.OutputStream.Write(buffer, 0, num);
 100:                 }
 101:  
 102:                 while (num > 0);
 103:  
 104:                 Response.Flush();
 105:             }
 106:         }
 107:     }
 108: }

After creating the application page, I added a SharePoint 2010 Empty Element item called DownloadZip to my project. This is nothing more than an Elements.xml file that takes care of adding my custom action to ribbon (CustomAction.Location=”CommandUI.Ribbon”). By default, for document libraries, this is what the ribbon looks like:

image

I wanted to add my action inside of the area in the Documents tab, inside of the Copies group so to do this, for the CommandUIDefinition, I set the Location attribute to "Ribbon.Documents.Copies.Controls._children”. I also wanted it to appear right after the Download a Copy action so for the Button element’s Sequence attribute, I set the value to 15 (the Download a Copy button has a sequence of 10 and the Send To button has a sequence of 20 so I needed to set the sequence of my button to something in between). To understand where everything is placed and what the sequences are by default, you need to look at the file C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\GLOBAL\XML\CMDUI.xml. I also specified the icons I wanted to use for my action (these icons are also part of my project, located in a sub-directory of the Images SharePoint mapped folder) and I also set the TemplateAlias to “o1” so that my icon shows up large like Download a Copy does. I also define the actual command handler in this Elements.xml file by adding a CommandUIHandler element. The CommandAction attribute is used to specify what exactly the button is supposed to do and the EnabledScript attribute is used to determine whether or not the button/command is enabled. These two attributes’ values both point to javascript functions I define in a separate file (discussed later). Because I’m using a separate javascript file, I also have to add another CustomAction element in the Elements file that points to the location of my javascript file. This is the result:

Enabled

image

Disabled

image

Below is the full Elements.xml file:

Elements.xml

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
   3:   <CustomAction Id="DeviantPoint.DownloadZip" Location="CommandUI.Ribbon">
   4:     <CommandUIExtension>
   5:       <CommandUIDefinitions>
   6:         <CommandUIDefinition Location="Ribbon.Documents.Copies.Controls._children">
   7:           <Button Id="Ribbon.Documents.Copies.DownloadZip"
   8:                   Command="DownloadZip"
   9:                   Sequence="15" 
  10:                   Image16by16="/_layouts/images/DeviantPoint.DownloadZip/zip_16x16.png" 
  11:                   Image32by32="/_layouts/images/DeviantPoint.DownloadZip/zip_32x32.png"
  12:                   Description="Download zip" LabelText="Download as Zip"
  13:                   TemplateAlias="o1"/>
  14:         </CommandUIDefinition>
  15:       </CommandUIDefinitions>
  16:       <CommandUIHandlers>
  17:         <CommandUIHandler
  18:           Command="DownloadZip"
  19:           CommandAction="javascript:downloadZip();"
  20:           EnabledScript="javascript:enable();"/>
  21:       </CommandUIHandlers>
  22:     </CommandUIExtension>
  23:   </CustomAction>
  24:   <CustomAction Id="Ribbon.Library.Actions.Scripts"
  25:                 Location="ScriptLink"
  26:                 ScriptSrc="/_layouts/DeviantPoint.DownloadZip/CustomActions.js" />
  27: </Elements>

Finally, I created the CustomActions.js file. This file is used to define the actions/behavior of my new ribbon button. The enable() function is used to determine whether or not my button is enabled. If there is at least one item selected, then my button is enabled. The downloadZip() function just starts off the download process. Actually, I could have probably written the javascript so I didn’t even need this function or calls to SP.ClientContext.executeQueryAsync() but I was just trying to get something done quickly and actually writing it this way gave me another place to show-off another one of the UI features, the Status. If the call to SP.ClientContext.executeQueryAsync() fails, then the onQueryFailed delegate is executed. The onQueryFailed() function uses the SP.UI.Status to display the error message, shown here:

download_failed

The function onQuerySucceeded() is where the majority of the action happens. I use the SP.ListOperation.Selection object to get a list of the selected items. I then create a request to my DownloadZip.aspx application page and send that page the list of selected item ids as well as the current url (the url of the page the user is on). Like I said earlier, that application page takes care of packaging everything up as a zip and streaming it down to the browser.

Below is the code for CustomActions.js:

CustomActions.js

   1: function enable() {
   2:     var items = SP.ListOperation.Selection.getSelectedItems();
   3:     var itemCount = CountDictionary(items);
   4:     return (itemCount > 0);
   5:  
   6: }
   7:  
   8: function downloadZip() {
   9:  
  10:     var context = SP.ClientContext.get_current();
  11:     this.site = context.get_site();
  12:     this.web = context.get_web();
  13:     context.load(this.site);
  14:     context.load(this.web);
  15:     context.executeQueryAsync(
  16:         Function.createDelegate(this, this.onQuerySucceeded),
  17:         Function.createDelegate(this, this.onQueryFailed)
  18:     );    
  19: }
  20:  
  21: function onQuerySucceeded() {
  22:  
  23:     var items = SP.ListOperation.Selection.getSelectedItems();
  24:     var itemCount = CountDictionary(items);
  25:  
  26:     if (itemCount == 0) return;
  27:  
  28:     var ids = "";
  29:     for (var i = 0; i < itemCount; i++) {
  30:         ids += items[i].id + ";";
  31:     }
  32:  
  33:     //send a request to the zip aspx page.
  34:     var form = document.createElement("form");
  35:     form.setAttribute("method", "post");
  36:     form.setAttribute("action", this.site.get_url() + this.web.get_serverRelativeUrl() + "/_layouts/deviantpoint.downloadzip/downloadzip.aspx");
  37:  
  38:     var hfSourceUrl = document.createElement("input");
  39:     hfSourceUrl.setAttribute("type", "hidden");
  40:     hfSourceUrl.setAttribute("name", "sourceUrl");
  41:     hfSourceUrl.setAttribute("value", location.href);
  42:     form.appendChild(hfSourceUrl);
  43:  
  44:     var hfItemIds = document.createElement("input")
  45:     hfItemIds.setAttribute("type", "hidden");
  46:     hfItemIds.setAttribute("name", "itemIDs");
  47:     hfItemIds.setAttribute("value", ids);
  48:     form.appendChild(hfItemIds);
  49:  
  50:     document.body.appendChild(form);
  51:     form.submit();
  52: }
  53:  
  54: function onQueryFailed(sender, args) {
  55:     this.statusID = SP.UI.Status.addStatus("Download as Zip:", 
  56:         "Downloading Failed: " + args.get_message() + " <a href='#' onclick='javascript:closeStatus();return false;'>Close</a>.", true);
  57:     SP.UI.Status.setStatusPriColor(this.statusID, "red");
  58: }
  59:  
  60: function closeStatus() {
  61:     SP.UI.Status.removeStatus(this.statusID);
  62: }

So how does this actually all look when the user is using it? Below is the hierarchy of an example document library I have:

image

  • Documents
    • Folder A (Folder)
      • Subfolder in Folder A (Folder)
        • Sub Sub Folder (Folder)
          • Versioning Flow (Visio diagram)
        • Business Brief_SoW (Word document)
        • SoW_Phase1 (Word document)
      • Request Email (Text file)
      • Users and Roles (Excel file)
    • Issues (Excel file)
    • Product_Planning (Excel file)

The user has selected some documents and a sub-folder so my custom ribbon button is enabled:

image

The user clicks on this button and this feature executes and after it’s complete the user is prompted with this (note, the filename is a timestamp):

image

The user saves it down locally to his computer and sees all the files are there:

image

That’s it, code is done. Time to drink some beer (I was actually drinking some beer as I wrote this so forgive any mistakes :)).

WSP file (Site collection feature): DeviantPoint.DownloadZip.wsp (84.84 kb)

Visual Studio 2010 Project file: DeviantPoint.DownloadZip.zip (317.01 kb)

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Currently rated 3.5 by 15 people

  • Currently 3.533334/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

SharePoint 2010

PowerShell Script for MOSS 2007 to Change Site Collections to Read-Only Mode

by Bart X. Tubalinal 5. May 2010 09:52

I wrote my very first PowerShell script today which I wanted to share. I’m definitely no PowerShell guru; in fact, the only times I’d ever used Powershell before is to run a script someone else has written. I have never written even one line of a PowerShell script before but I had to do so today. Basically, I needed to write something to quickly turn all my MOSS 2007 site collections into read-only mode. Since we’re talking about hundreds of site collections here, there was no way I was going to do this manually. In Service Pack 2 for WSS/MOSS, the setsitelockSTSADM operation was added to allow you to specify a lock type on a site collection. I knew I could use this and combine it with the enumsitesSTSADM operation to quickly turn my site collections into read-only mode and then later turn it back into ‘normal’ mode.

Below is my script. Remember that again, this is my first PowerShell script and I needed to get it done quickly so be gentle in the critique ;).

Script: setsitelock.ps1

   1: param([string]$lockmode, [string[]]$webappurls)
   2:  
   3: $arrLockModes = "none", "noadditions", "readonly", "noaccess"
   4:  
   5: if ($lockmode -eq "" -or $webappurls -eq $null)
   6: {
   7:     Write-Host -foregroundcolor red "Either lockmode or webappurls not specified. Usage: setsitelock -mode {none|noadditions|readonly|noaccess} -webappurls url1,url2,url3"
   8: }
   9: elseif ($arrLockModes -notcontains $lockmode)
  10: {
  11:     Write-Host -foregroundcolor red "Incorrect -lockmode specified. Valid lockmodes are: $arrLockModes"
  12: }
  13: else
  14: {
  15:     foreach($webappurl in $webappurls)
  16:     {
  17:         Write-Host "Setting site lock for site collections in $webappurl to $lockmode."
  18:         $rawdata=stsadm -o enumsites -url $webappurl
  19:         $sitesxml=[XML]$rawdata
  20:         
  21:         foreach($site in $sitesxml.sites.site)
  22:         {
  23:             $scurl= $site.url
  24:             Write-host -foregroundcolor yellow "`tSite collection: $scurl"
  25:             $msg = stsadm -o setsitelock -url $scurl -lock $lockmode
  26:             
  27:             if ($msg -match "Operation completed successfully") {$fgcolor="Green"}
  28:             else {$fgcolor="Red"}
  29:  
  30:             Write-host -ForegroundColor $fgcolor "`t`t$msg"
  31:         }
  32:     }
  33: }

To run the script, just save the script to a directory, open up the PowerShell console, navigate to the directory and run:

.\setsitelock.ps1 –lockmode {none|noadditions|readonly|noaccess} –webappurls “app_url_1”, “app_url_2”, “app_url_3”, etc.

 The actual file: setsitelock.zip (624.00 bytes)

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Currently rated 3.0 by 5 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

SharePoint | SharePoint Configuration | PowerShell

ALM with Team Foundation Server 2010 and SharePoint 2010 (Part 2)

by Bart X. Tubalinal 25. April 2010 15:18

In the first part of this series, I showed you how to create a new Team Project Collection in TFS and the new SharePoint site collection and SSRS reports folder created for the site collection. In this part, I will show you how to create a TFS Team Project in the Team Project Collection, add team members to the project, manage permissions on the SharePoint site and review the SSRS report security settings.

Part 2A: Creating a team project.

1. Open Visual Studio 2010 and click on Connect to Team Foundation Server.

 image

2.  Select the TFS server and select the Team Project Collection that we created in Part 1 and click the Connect button.

image

3. Create a new Team Project in the Team Project Collection. In the Team Explorer pane, right-click on the Team Project Collection and select New Team Project.

image 

4. In the wizard that opens, specify the name and description of the new project.

image

5. Specify the Process Template to use for the project. Out of the box, there are two process templates available: MSF for Agile Software Development v5.0 and MSF for CMMI Process Improvement v5.0. For this project, I’m going to use the MSF for Agile Software Development v5.0 template. To understand the differences between the two templates, refer to the documentation located here: http://msdn.microsoft.com/en-us/vstudio/aa718795.aspx. Note, you can also download additional process templates or create your own process template using the Process Editor (part of the TFS Power Tools). This is available at the above link.

image 

6. Allow the New Project wizard to create the SharePoint site that will be used as the portal for this project. The site will be created under the site collection that was created for the Team Project Collection.

image

7. The wizard will create a new source control folder in TFS.

image

8. Review the new Team Project settings.

image

9. Click the Finish button.

image

10. Close the wizard after the project is created.

image

Part 2B: Managing Team Members and Permissions

1. In Team Explorer, find the new project we just created and right-click on Team Members. You can give users access to the team by clicking on the Add Team Member  option. You can also create sub-teams by selecting the New Subteam option. For this project,  we will create the following subteams: Business Analysts, Developers, Project Managers, and Testers.

image

2. Add the appropriate members to each sub-team by right-clicking on each sub-team name and selecting the Add Team Member option from the menu. These members will all be added to the Team Project’s Contribute security group. To modify or reassign them to a different security group, right-click on the team project name and select Team Project Settings –> Group Membership.

image

3. In order to make sure the team members can access the project portal, you need to go into the SharePoint site and give the users you added to the team permissions to the site. In the Team Explorer window, right-click on the project name and select Show Project Portal. This will open the SharePoint site that was created for this project.

image

14. Go to Site Actions –> Site Permissions to grant access to this site for the project team members. You can create groups, custom permission levels, etc just like permission management in any other SharePoint site. In this case, I’m going to just grant each team member Contribute permissions for this site.

image 

Part 2C: Reviewing SSRS Security

1. In the SharePoint site, click the Reports link in the Quick Launch menu on the left. You will be taken to the SSRS reports folder specific to the new project. There different folders for the different reports. The reports available are based on the process template you selected when creating the Team Project. By default, for the Agile-based process template, there are reports for Bugs, Builds, Project Management, and Tests.

image

2. You can also manage the security of a report folder or report. Hover over a folder or report and select Security from the drop-down menu. Select Edit Item Security from the toolbar. You will be asked to confirm that you actually want to break permission-inheritance for the object.

image

image

3. By either clicking on the New Role Assignment toolbar item or clicking Edit next to an existing Group or User, you can change with Role Assignment for the group or user. The main roles are: Browser (read-access), Publisher (create reports), and Content Manager (manages the report server content for the project).

image

image

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Currently rated 1.5 by 2 people

  • Currently 1.5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

SharePoint | SharePoint 2010 | SQL | Team Foundation Server

ALM with Team Foundation Server 2010 and SharePoint 2010 (Part 1)

by Bart X. Tubalinal 25. April 2010 11:31

Team Foundation Server (TFS) 2010 and SharePoint 2010 are excellent tools to use for managing projects and applications. In this series on Application Lifecycle Management (ALM), I will show you from start to finish how to manage your projects in TFS 2010 and SharePoint 2010. For this example, I will use these tools to manage the creation of a new SharePoint 2010 intranet portal for my company, DeviantPoint.

This series does not cover installation and configuration of TFS, SharePoint 2010, and SQL Server 2008 R2 Reporting Services. Those are topics I may cover in a future post. However, here are some fairly good resources that should help you get started with that:

Part 1: Creating a Team Project Collection

1. Open the Team Foundation Administration Console.
2. Create a new collection under Server –> Application Tier –> Team Project Collections.
3. Click Create Collection.

image

4. Provide a name and description for the new Project Collection.

image


5. Specify the data tier (SQL Server and database name).

image


6. Specify the SharePoint web application and path to new site collection.

image


7. Specify the SQL Server Reporting Services server. Note that TFS doesn’t work with SSRS SharePoint Integrated mode. In my environment, I have two SSRS servers, one that runs in SharePoint Integrated mode (for my BI-related demos) and one that doesn’t. Here, I’m specifying the URL to the Reporting Services server that doesn’t run in integrated mode.

image 


8. Configure the Lab Management (not currently configured in my environment)

image


9. Review the Configuration and make changes as necessary.

image


10. Run Readiness Check.

image


11. Create the new Team Project Collection.

image


12. Close the wizard.

image

12. Review the top-level site collection created in SharePoint.

image

13. Review SQL Server Reporting Services.

image

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

SharePoint | SharePoint 2010 | SQL | Team Foundation Server

Microsoft FAST Search Server 2010 Installation Issue with Powershell Script

by Bart X. Tubalinal 10. March 2010 11:43

I am currently installing Microsoft FAST Search Server 2010 RC on my demo/development environment and ran into an issue running the post configuration Powershell script (found at C:\FastSearch\installer\scripts\psconfig.ps1). When I initially ran the script, it failed either because I didn’t supply the FQDN to my SQL Server machine (which is required) or because I may have fat-fingered the service account name or one of the associated passwords. Anyways, when I tried to re-run the script (this time making sure I entered the inputs correctly), I got an error that the configuration failed, specifically that ‘An error occurred while installing Resource store’. So I did a search both on the web and the Microsoft Connect newsgroups to see if there was anyone else that had this error but saw nothing. I checked the logs and found this (not the complete log file):

   1: 3/10/2010 4:53:51 PM Verbose Utility.Execute - "C:\FASTSearch\bin\ResourceStoreInstaller.exe" Output - (Mar 10, 2010 04:53:51 PM), Fast-installer, resourcestoreinstaller.exe, C:\Windows\system32\inetsrv\appcmd.exe SET config "ResourceStore/Config" /section:requestFiltering /+verbs.[verb='POST',allowed='true'] /commit:apphost - Applied configuration changes to section "system.webServer/security/requestFiltering" for "MACHINE/WEBROOT/APPHOST/ResourceStore/Config" at configuration commit path "MACHINE/WEBROOT/APPHOST"  
   2: 3/10/2010 4:53:51 PM Verbose Utility.Execute - "C:\FASTSearch\bin\ResourceStoreInstaller.exe" Output - (Mar 10, 2010 04:53:51 PM), Fast-installer, resourcestoreinstaller.exe, C:\Windows\system32\inetsrv\appcmd.exe SET config "ResourceStore/Config" /section:requestFiltering -requestLimits.maxAllowedContentLength:1073741824 /commit:apphost - Applied configuration changes to section "system.webServer/security/requestFiltering" for "MACHINE/WEBROOT/APPHOST/ResourceStore/Config" at configuration commit path "MACHINE/WEBROOT/APPHOST"  
   3: 3/10/2010 4:53:51 PM Verbose Utility.Execute - "C:\FASTSearch\bin\ResourceStoreInstaller.exe" Output - (Mar 10, 2010 04:53:51 PM), Fast-installer, resourcestoreinstaller.exe, C:\Windows\system32\inetsrv\appcmd.exe SET config "ResourceStore/Config" /section:httpRuntime -maxRequestLength:1024000 -executionTimeout:3600 /commit - Applied configuration changes to section "system.web/httpRuntime" for "MACHINE/WEBROOT/APPHOST/ResourceStore/Config" at configuration commit path "MACHINE/WEBROOT/APPHOST/ResourceStore/Config"  
   4: 3/10/2010 4:53:51 PM Warning Utility.Execute - "C:\FASTSearch\bin\ResourceStoreInstaller.exe" StdError - resourcestoreinstaller.exe - C:\Windows\system32\inetsrv\appcmd.exe SET config "ResourceStore/Config" /section:appSettings /+"[key='FileLimit',value='']" /commit - ERROR ( message:New add object missing required attributes. Cannot add duplicate collection entry of type 'add' with unique key attribute 'key' set to 'FileLimit'
   5: 3/10/2010 4:53:51 PM Warning Utility.Execute - "C:\FASTSearch\bin\ResourceStoreInstaller.exe" StdError -  . )  
   6: 3/10/2010 4:53:51 PM Warning Utility.Execute - Return code for binary "C:\FASTSearch\bin\ResourceStoreInstaller.exe" is not 0. This may indicate that binary didn't execute successfully
   7: 3/10/2010 4:53:51 PM Verbose Utility.Execute - Finished executing "C:\FASTSearch\bin\ResourceStoreInstaller.exe"
   8: 3/10/2010 4:53:51 PM Error InstallResourceStore - An error occurred while executing binary "C:\FASTSearch\bin\ResourceStoreInstaller.exe". Return code is not 0.
   9: 3/10/2010 4:53:51 PM Error Utility.WriteException - Exception -  : Exception - System.Management.Automation.RuntimeException: An error occurred while installing Resource store.

The exception is on line 9 but the important error is actually on line 4. Based on this line, I was able to deduce that what was happening was the resource installer was trying to add a key (FileLimit) in an appSettings section of a config file but couldn’t because the key already existed. Instead of just replacing the old key, it just causes the post configuration script to fail. Anyways, in order to fix it, I found the config file that it was attempting to update (there are a ton of config files for this app). The particular config file where it was failing is located at C:\FASTSearch\components\resourcestore\config\web.config. Sure enough, the FileLimit key was there. All I did was delete that key, re-ran the script again and presto, all done.

Anyways, just wanted to share this, to save others the headache I just ended up getting trying to figure out why that darn script wasn’t working correctly.

Technorati Tags: ,
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

SharePoint 2010 | FAST Search

Example: Using the SharePoint 2010 Silverlight Client Object Model and Excel Services REST API

by Bart X. Tubalinal 7. February 2010 05:20

NOTE: This code is based on the SharePoint 2010 Release Candidate version.

Download: ExcelChartViewerSL.zip (975.87 kb) 

I finally had a chance to put together a little sample of how to use the SharePoint 2010 Silverlight Client Object Model which I wanted to share. Now this example could have been just as easily done with jQuery and the Client OM for Javascript but I really didn't want to write another post on jQuery (I think everyone knows how much I like it) and instead, I wanted to show how much I like Silverlight and WPF as well J (so much so that I'm actually currently writing a Facebook desktop application in WPF).

In this example, I built a Silverlight app that can be used to display charts inside of an Excel workbook. The concept I had was simple, really. I wanted to take all charts contained in an Excel workbook located in a SharePoint document library and display them as navigational elements first (as thumbnails of the chart) and after one was selected, to display the entire chart itself. I thought this would be a good starting point to show Silverlight + SharePoint + Excel and an example that can be extended further for use by anyone. So this is the mockup for what I intended to build:

Figure 1 – Mockup

The first thing to do is to open Visual Studio 2010 and create a new Silverlight Application Project. The wizard will ask you if you want to add a Web project to the solution as well. For this example, it is not required. The project will contain two XAML files, an App.xaml and a MainPage.xaml. The App.xaml is for handling the application itself and the MainPage.xaml is for the main UI component of the application.

With MainPage.xaml, make sure to name the UserControl as this will later be used as part of some of the bindings defined in controls later. In this case, I named the control 'thisControl' (line 1). Then I defined the layout of the control by adding three grid row definitions to the LayoutRoot grid container (lines 8-12). All have been defined with a width of Auto so that each row will collapse if there is nothing inside of them. Then, I dropped an Image control and a TextBlock control in the xaml. The Image control, named 'imgMain', targets the second row of the grid container (line 37, Grid.Row="1") and the TextBlock control, 'tbStatus', targets the third row of the container (line 43, Grid.Row="2").

Look back at the Image control. In lines 40-41, I am defining a maximum height and width for the image by binding it to properties I defined in this MainPage user control class (MainPage.xaml.cs). The properties are MainImageMaxHeight and MainImageMaxWidth, both of which are ints. The maximum height and width has a default that I set (later) but can be modified by the user. Notice also that for each binding's ElementName, I specified the name I gave to the control itself, 'thisControl'. In line 38, I am binding the Source to a property called ID. Now this ID property isn't part of my MainPage user control class. It is actually a property of another class I defined called ExcelChart (also inside of the MainPage.xaml.cs) file. The ExcelChart class has two properties: ID (the url for the chart object) and Title. An instance of the ExcelChart class is what is used as the DataContext for the image, which you will see in the code.

For the thumbnails row, all I'm using is a ListBox control. This is an extremely versatile control. The first thing I want to make sure of is to make sure that the items in the listbox are presented horizontally rather than vertically as is the default. So in lines 16-21, I define the template used for the items panel (ItemsPanelTemplate) to use a StackPanel as the container with Orientation set to Horizontal. Then, I need to define how the actual items in the listbox will be displayed. For that, I defined the ItemTemplate of the listbox (lines 21-34). The template uses a two-row grid as the container. The first row contains a TextBlock with the chart's title and the second row contains an Image control that displays the chart. Notice the binding for the TextBlock's text uses the Title property and the Image source uses the ID property of the ExcelChart class.

The only other thing to pay attention to in the MainPage.xaml are the event handlers. There are only two events I care about, when the control gets loaded (line 5) and when the selection has changed on the list box (line 15).

Here is the full MainPage.xaml:

MainPage.xaml
    1 <UserControl x:Class="ExcelChartViewerSL.MainPage" Name="thisControl"

    2    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    3    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    4    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    5    Loaded="UserControl_Loaded" Height="Auto" Width="Auto">

    6 

    7     <Grid x:Name="LayoutRoot" Background="White">

    8         <Grid.RowDefinitions>

    9             <RowDefinition Height="Auto"/>

   10             <RowDefinition Height="Auto"/>

   11             <RowDefinition Height="Auto"/>

   12         </Grid.RowDefinitions>

   13 

   14         <ListBox Name="lbCharts" Grid.Row="0" BorderBrush="Transparent" HorizontalAlignment="Center"

   15                 SelectionChanged="lbCharts_SelectionChanged">

   16             <ListBox.ItemsPanel>

   17                 <ItemsPanelTemplate>

   18                     <StackPanel Orientation="Horizontal"/>

   19                 </ItemsPanelTemplate>

   20             </ListBox.ItemsPanel>

   21             <ListBox.ItemTemplate>

   22                 <DataTemplate>

   23                     <Grid>

   24                         <Grid.RowDefinitions>

   25                             <RowDefinition Height="Auto"/>

   26                             <RowDefinition Height="Auto"/>

   27                         </Grid.RowDefinitions>

   28                         <TextBlock Text="{Binding Title}" FontWeight="Bold" HorizontalAlignment="Center" />

   29                         <Image Source="{Binding ID}" Grid.Row="1"

   30                               MaxWidth="250" MaxHeight="250"

   31                               Margin="3,2,3,2" HorizontalAlignment="Center"/>

   32                     </Grid>

   33                 </DataTemplate>

   34             </ListBox.ItemTemplate>

   35         </ListBox>

   36 

   37         <Image Name="imgMain" Grid.Row="1"

   38               Source="{Binding ID}"

   39               Margin="0, 10, 0, 0" HorizontalAlignment="Center"

   40               MaxHeight="{Binding ElementName=thisControl, Path=MainImageMaxHeight}"

   41               MaxWidth="{Binding ElementName=thisControl, Path=MainImageMaxWidth}"/>

   42 

   43         <TextBlock Name="tbStatus" Grid.Row="2" HorizontalAlignment="Left" TextWrapping="Wrap" Text="" Visibility="Collapsed"/>

   44 

   45     </Grid>

   46 </UserControl>

   47 

 

Now take a look at MainPage.xaml.cs (shown below). In lines 22-26 is where I define the simple ExcelChart class with my two properties, ID and Title. In the main page class, I defined three properties (lines 34-36), WorkbookLocation, MainImageMaxWidth, and MainImageMaxHeight. The MainImageMaxWidth/Height I already discussed. The WorkbookLocation is used to specify which workbook to get the charts from.

In the UserControl_Loaded event handler (starting at line 45), the first thing I want to make sure of is that the WorkbookLocation is set. At the very least, that needs to be specified. If not, show an error message and stop. If it is specified, we then call the BeginLoad() method to start processing.

In the BeginLoad method (lines 61-68), I use the SharePoint client OM to request the workbook file. The client OM is new for SharePoint 2010. If you haven't already heard, the difference with the client OM and the server OM is that with the client OM, the objects have to be explicitly loaded. In other words, I can't just start trying to access properties of a SharePoint object as those properties won't be available right away. So in this case, I have this _wkbkFile object (of type Microsoft.SharePoint.Client.File) I defined as a field of my MainPage class. Before I can do anything with this object, I need to load it by explicitly asking the ClientContext object to load it (and execute a query) for me (lines 65-67). Note that with the Silverlight OM, the queries have to be done asynchronously.

When you call ExecuteQueryAsync, it expects two callbacks, one for if the query succeeds and one for if the query fails. The callback I defined for the failure is between line 76-88. If the query failed, then I just display a message to the user about the failure. An important thing to remember here is that when the query is called and is executing, it is executing on a different thread. It is not executing on the UI thread. The implication here is that in order to update the TextBlock text to display the status message, I need to make sure I do that on the UI thread. In order to do this, we use Dispatcher.BeginInvoke() to wrap the call that updates the text and changes the TextBlock's visibility.

The succeeded callback (_wkbkFile_LoadSucceeded) simply calls the LoadWorkbookFeed() method (lines 90-99). In SharePoint 2010, Excel Services has added the ability to expose objects contained in an Excel workbook via RESTful services. In this case, I create a string (wkbkRestUrl) to the Excel REST service that will return to me all the charts in a particular workbook (line 93). Then, I use a System.Net.WebClient object to request that URL. The WebClient also makes the request asynchronously so I have to provide a callback function that should be executed when the request completes (wcFeedReader_OpenReadCompleted).

When you call this Excel REST service the response is going to be in ATOM format. In wcFeedReader_OpenReadCompleted (lines 101-134), I use an XDocument object to load up the response that comes back from the REST service. I then use LINQ to XML (lines 116-122) to get an IEnumerable<ExcelChart> collection of all entry objects. Finally, I set the ItemsSource property of my ListBox (lbCharts) to my 'entries' collection, select the first chart, and set the DataContext of my imgMain image to the selected ExcelChart object. Again, since I am now updating something with the UI and because I am currently executing on a different thread than the UI thread, I need to make sure to wrap the code that updates the UI with a call to Dispatcher.BeginInvoke().

Here's the full MainPage.xaml.cs:

MainPage.xaml.cs

   19 namespace ExcelChartViewerSL

   20 {

   21 

   22     public class ExcelChart

   23     {

   24         public string ID { get; set; }

   25         public string Title { get; set; }

   26     }

   27 

   28     public partial class MainPage : UserControl

   29     {

   30         private const string EXCEL_REST_URL = "/_vti_bin/ExcelRest.aspx/";

   31         private const string MODEL_CHART_URL = "/Model/Charts/";

   32         private const string NS_ATOM = "http://www.w3.org/2005/Atom";

   33 

   34         public string WorkbookLocation { get; set; }

   35         public int MainImageMaxWidth { get; set; }

   36         public int MainImageMaxHeight { get; set; }

   37 

   38         private Microsoft.SharePoint.Client.File _wkbkFile;

   39 

   40         public MainPage()

   41         {

   42             InitializeComponent();

   43         }

   44 

   45         private void UserControl_Loaded(object sender, RoutedEventArgs e)

   46         {

   47             //make sure the web part was actually configured with a workbook location.

   48             if (string.IsNullOrEmpty(WorkbookLocation))

   49             {

   50                 tbStatus.Text = "Please configure at least the workbook location. Init Params Available: " +

   51                     "workbookLocation(string), mainImageMaxHeight(int), mainImageMaxWidth(int)";

   52 

   53                 tbStatus.Visibility = Visibility.Visible;

   54                 return;

   55             }

   56 

   57             //start the loading of the workbook file

   58             BeginLoad();

   59         }

   60 

   61         private void BeginLoad()

   62         {

   63             //first, use the clientcontext object to request the workbook file

   64             _wkbkFile = ClientContext.Current.Web.GetFileByServerRelativeUrl("/sites/adventureworksbi" + "/" + this.WorkbookLocation);

   65             ClientContext.Current.Load(_wkbkFile);

   66 

   67             ClientContext.Current.ExecuteQueryAsync(_wkbkFile_LoadSucceeded, _wkbkFile_LoadFailed);

   68         }

   69 

   70         private void _wkbkFile_LoadSucceeded(object sender, ClientRequestSucceededEventArgs e)

   71         {

   72             //file load succeeded. start loading the workbook REST feed

   73             LoadWorkbookFeed();

   74         }

   75 

   76         private void _wkbkFile_LoadFailed(object sender, ClientRequestFailedEventArgs e)

   77         {

   78             //file loading failed. show the error

   79             //Dispatcher.BeginInvoke is used because this is currently not executing on the UI thread

   80             Dispatcher.BeginInvoke(delegate()

   81             {

   82                 this.tbStatus.Text = "Error message: " + e.Message

   83                     + "\nWorkbook file: " + WorkbookLocation

   84                     + "\nCode: " + e.ErrorCode.ToString();

   85 

   86                 this.tbStatus.Visibility = Visibility.Visible;

   87             });

   88         }

   89 

   90         private void LoadWorkbookFeed()

   91         {

   92             //the feed url format is: http://{server/sitecollection}/{site}/_vti_bin/ExcelRest.aspx/{workbooklocation}/model/charts

   93             string wkbkRestUrl = ClientContext.Current.Url + EXCEL_REST_URL + WorkbookLocation + MODEL_CHART_URL;

   94 

   95             //use a web client object to request the workbook REST feed

   96             WebClient wcFeedReader = new WebClient();

   97             wcFeedReader.OpenReadCompleted += new OpenReadCompletedEventHandler(wcFeedReader_OpenReadCompleted);

   98             wcFeedReader.OpenReadAsync(new Uri(wkbkRestUrl));

   99         }

  100 

  101         void wcFeedReader_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)

  102         {

  103             //the response sent back will be ATOM. load the response in an XDocument first.

  104             XDocument doc;           

  105             using (Stream s = e.Result)

  106             {

  107                 doc = XDocument.Load(s);

  108             }

  109 

  110             //ATOM is the default namespace in the response.

  111             //we need to prepend this when referring to any elements or attributes of the document

  112             XNamespace nsAtom = XNamespace.Get(NS_ATOM);

  113 

  114             //use LINQ to XML to get all 'entry' objects in the feed

  115             //with each 'entry' element, create an instance of our ExcelChart class (defined above)

  116             var entries =

  117                 from entry in doc.Root.Descendants(nsAtom + "entry")

  118                 select new ExcelChart()

  119                 {

  120                     ID = entry.Element(nsAtom + "id").Value,

  121                     Title = entry.Element(nsAtom + "title").Value

  122                 };

  123 

  124             //remember that the REST feed request was started on a different thread.

  125             // we need to use Dispatcher.BeginInvoke here to update the UI thread.

  126             Dispatcher.BeginInvoke(delegate()

  127             {

  128                 this.lbCharts.ItemsSource = entries;

  129                 if (this.lbCharts.Items.Count > 0) this.lbCharts.SelectedIndex = 0;

  130 

  131                 this.imgMain.DataContext = this.lbCharts.SelectedItem;

  132             });

  133 

  134         }

  135 

  136         private void lbCharts_SelectionChanged(object sender, SelectionChangedEventArgs e)

  137         {

  138             this.imgMain.DataContext = this.lbCharts.SelectedItem;

  139         }

  140     }

  141 }

  142 

 

Lastly, I need to edit the App.xaml.cs. There's not much to explain here. I'm just retrieving the initparams that is passed by the Silverlight host object and using it to set up my application.

App.xaml.cs (Application_Startup)

   27         private void Application_Startup(object sender, StartupEventArgs e)

   28         {

   29             //Load up any init parameters from the silverlight web part host.

   30             IDictionary<string, string> parms = e.InitParams;

   31 

   32             string workbookLocation = parms.ContainsKey("workbookLocation") ? parms["workbookLocation"] : string.Empty;

   33             int mainImageMaxHeight = parms.ContainsKey("mainImageMaxHeight") ? int.Parse(parms["mainImageMaxHeight"]) : 400;

   34             int mainImageMaxWidth = parms.ContainsKey("mainImageMaxWidth") ? int.Parse(parms["mainImageMaxWidth"]) : 640;

   35 

   36             //Create our main user control (MainPage) and set it up as the app's main UI.

   37             MainPage mainPage = new MainPage()

   38             {

   39                 WorkbookLocation = workbookLocation,

   40                 MainImageMaxHeight = mainImageMaxHeight,

   41                 MainImageMaxWidth = mainImageMaxWidth

   42             };

   43 

   44             this.RootVisual = mainPage;

   45         }

 

After the project is built, then all you need to do is drop the XAP file into the ClientBin directory (c:\program files\common files\microsoft shared\web server extensions\14\templates\layouts\clientbin). I actually created a subdirectory structure (DeviantPoint\ExcelChartViewerSL) under clientbin to store my xap for this application. Then, drop a Silverlight web part (found under Media and Content) and configure it to point to the new XAP file.

Figure 2 – Add a Silverlight Web Part

 

Figure 3 – Configure the web part to point to the XAP

 

Figure 4 – Configuring the appearance

 

Figure 5 – Configuring other settings, which contains the Initialization Parameters passed to the Silverlight application

(Full Initialization Parameters: workbookLocation=AnalyticsReports/AW Charting Samples.xlsx,mainImageMaxHeight=500,mainImageMaxWidth=800)

So that's it. It was pretty simple to build, actually. It probably took me about an hour or so to get it all working the way I expected it to. Here's a screenshot of the Silverlight web part with my Silverlight app:

Figure 6 – Silverlight web part for displaying Excel charts


Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

Excel Services | LINQ | REST | SharePoint Development | SharePoint 2010 | Silverlight