Tuesday, August 31, 2010

Development Servers

I recently built a home development environment, basically an upgrade from what I had. 

The new setup includes 3 physical machines:

  1. Domain Controller / Hyper-V server
    • Intel i7 920 @ 2.67 Ghz
    • Asus P6T – had to install newest Bios update to get the RAM to post
    • Currently 18GB DDR3 1333Mhz Ram (Going to be 24 GB soon enough)
    • OCZ 12 GB Kit – OCZ3G1333LV12GK
    • Corsair 6GB kit
    • RAID 5 – 500 GB WD 7200 rpm drives
    • 2 x 1.5 TB Seagate Data drives
    • Windows Server 2008 R2 x64
      • Domain Controller
      • Hyper V Role
        • Exchange 2010
        • SharePoint 2007 Development Box
        • SharePoint 2010 Development Box
        • CRM
  2. SQL Server
    • Intel Q6600 @ 2.8Ghz
    • Abit Ip35 Pro
    • 8 GB Corsair DDR2 800Mhz RAM
    • RAID 5 – 500 GB WD 7200 rpm drives
    • Windows Server 2008 R2 x64
    • SQL Server 2008 R2
      • Instance for SP2007
      • Instance for SP2010
      • Instance for CRM
      • SSRS/SSIS enabled for each
  3. Workstation
    • AMD FX64 @ 2.6 GHz
    • Asus MB
    • 8 GB Corsair DDR2 800MHz RAM
    • Single 250 GB Seagate 7200 rpm HD
    • Windows 7 x64

The nice thing about the setup? Nothing really has to run on my local machine.  I occasionally run Visual Studio to look at files, or sync my phone with it, etc.  It is a powerful enough dumb terminal for me though.  I browse web pages on it, and really just remote desktop into the dev server I am going to work on.  Another great thing is that I have not really invested all that much into it.  Each time I feel the itch to build a new system (about every year to two) I get a solid motherboard, decent processor, fill half the banks of RAM(depending on price I might fill in all the banks), cheap video card, 1 to 2  hard drives (a 500 GB drive at the time of this article is ~50-60 bucks), a cheap OEM CD/DVD drive, Case (if I need one) and beast of a power supply (usually spend about $100 on a PSU even if it is only 500-600W).  All in all, I am usually only out 500-750 dollars and will reuse some lesser important components from the machine being trashed, or clear it and donate it to a local school/church.  Hard drives are cheap enough these days, I would highly recommend that you replace primary drives every year.  $50 bucks goes a long way to save your photos/documents/etc. 

Rundown of costs to build a new machine from scratch
100-150 Motherboard
100-200 Proc
100-200 Memory
100 – Power Supply
50-100 Hard drive(s)
30-50 Video Card
50 – Case
25 – CD/DVD

Range $555 - $875, again if you save a case, CD/DVD, video card, etc.  you get a decently new machine for a pretty good price.

Thursday, June 3, 2010

SharePoint 2010 Custom Columns now with XSL

If you are familiar with Custom Columns or Custom Fields in SharePoint 2007, they will feel very similar in 2010.  They follow the same basic steps for creating a column/field.  The one big change is that the Rendering template has been deprecated, and replaced with XSL to perform the same task.  This provides much more flexibility, and with a little jQuery, you can accomplish things that were not possible before.

Problem:

I want a list to hold configuration values for my web parts and read the values in when a web part is rendered.  One of the columns/fields in this list would need to be the web part type.  I could have the user type the web part type, but this is too prone for errors.  I want to show a drop down of items of available web parts in the site collection, storing the type, but displaying a user friendly value.  I am going to use SPFieldText for storage, but when looking at the list in a view, I want to show something meaningful.

Solution:

Create a custom field/column definition, add this type to my list to expose currently available web parts.  Create an XSL view to show my field, inserting an ID that I can use with jQuery and the SharePoint JavaScript\ECMA Client Object Model to lookup a friendly name.

Steps:

Create a new Visual Studio SharePoint Project:

image

Select Farm Solution:

image

Add a reference to System.Web:

image

image 

Now we need to add some “Mapped” folders.  Visual Studio 2010 with the addition of SharePoint project types also adds options for mapping to SharePoint folders.  These map to the 14 hive within the WSP and SharePoint will handle placing the files on the filesystems for you.

image

For the first folder, select “Template\XML” - This will house the field definition file.

image 

Add another Mapped folder for “Template\Layouts\XSL” – This will contain the XSL files for rendering our field.

image

Now we need to add a couple of classes. 

1. WebPartField.cs and WebPartFieldControl.cs

image

image

In the WebPartField class mark the class as public, derive from SPFieldText, and add the following:

using Microsoft.SharePoint;



using Microsoft.SharePoint.WebControls;



public class WebPartField : SPFieldText



{



    public WebPartField(SPFieldCollection fields, string fieldName)



        : base(fields, fieldName)



    {



    }



 



    public WebPartField(SPFieldCollection fields, string typeName, string displayName)



        : base(fields, typeName, displayName)



    {



    }



 



    public override BaseFieldControl FieldRenderingControl



    {



        get



        {



            BaseFieldControl fieldControl = new WebPartFieldControl(this);



            fieldControl.FieldName = InternalName; return fieldControl;



        }



    }



}





Save the file.




In the WebPartFieldControl.cs file, mark the class public, derive from BaseFieldControl, and add the following:





using System.Web.UI.WebControls; 



using System.Web.UI; 



using Microsoft.SharePoint; 



using Microsoft.SharePoint.WebControls; 



 



private WebPartField field;                



private DropDownList ddlWebParts;         



public WebPartFieldControl(WebPartField parentField)         



{             



this.field = parentField;             



this.ddlWebParts = new DropDownList();         



}         



 



protected override void CreateChildControls()         



{             



base.CreateChildControls();              



if (ControlMode == Microsoft.SharePoint.WebControls.SPControlMode.Display)                 



return;              



ddlWebParts = new DropDownList();             



ddlWebParts.CssClass = "ms-long";             



ddlWebParts.ClearSelection();             



ddlWebParts.Items.Clear();              



// we need to fill the drop down list with the values for sites in this site collection.             



foreach (SPListItem webpart in SPContext.Current.Site.GetCatalog(SPListTemplateType.WebPartCatalog).Items)



{                 



ddlWebParts.Items.Add(new ListItem(webpart.Title, webpart.Properties[""].ToString()));       



}            



this.Controls.Add(ddlWebParts);    



}         



public override object Value    



{             get       



{                 EnsureChildControls();    



return this.ddlWebParts.SelectedItem.Value;        



}          



set         



{           



EnsureChildControls();   



ListItem item = ddlWebParts.Items.FindByValue(Convert.ToString(value));  



if (item != null)         



{                  



ddlWebParts.SelectedIndex = ddlWebParts.Items.IndexOf(item);  



}      



}      



}      



public override void UpdateFieldValueInItem()  



{       



this.EnsureChildControls();   



try         



{           



string value = this.ddlWebParts.SelectedItem.Value;      



this.Value = value;                          



this.ItemFieldValue = Value;            



}          



catch (Exception)     



{            



}        



}          



protected override void Render(HtmlTextWriter output) 



{          



try            



{            



// If we are not in edit mode.                 



if (ControlMode == Microsoft.SharePoint.WebControls.SPControlMode.Display) 



{                  



if (!String.IsNullOrEmpty(this.ItemFieldValue.ToString()))    



{ 



output.WriteLine(SPContext.Current.Site.GetCatalog(SPListTemplateType.WebPartCatalog).GetItemById(Convert.ToInt32(this.ItemFieldValue.ToString())).Title);



}              



}            



else           



{          



ddlWebParts.RenderControl(output);  



}           



}           



catch (Exception) 



{             



}   



}




You can see in the code above, we are using a DropDownList control to render the web parts for us.  In the Render method, we are only showing the control if the item is in edit mode, otherwise we are showing the Title.



Now we have the fields ready to go in code, but we need to add the definition for SharePoint to pickup.



Right click on the mapped XML folder and select add new item, here we want a descriptive XML file “fldtypes_Custom.WebPartsList.xml”



image



Make sure the name is fldtypes_ or SharePoint will not pickup the field.



In the XML file, we are going to define things like what we want to call the column, description, where to show the column, but most importantly the class that defines it.  Notice in this case, we have the Namespace + Class name, then the token for the assembly name.  This token will gather the Public Key Token, culture, assembly name, etc. so we don’t have to.





<?xml version="1.0" encoding="utf-8" ?>



<FieldTypes>



  <FieldType>



    <Field Name="TypeName">WebPartList</Field>



    <Field Name="ParentType">Text</Field>



    <Field Name="TypeDisplayName">WebPart List</Field>



    <Field Name="TypeShortDescription">WebPart List</Field>



    <Field Name="UserCreatable">TRUE</Field>



    <Field Name="FieldTypeClass">CustomColumn.WebPartList.WebPartField, $SharePoint.Project.AssemblyFullName$</Field>



  </FieldType>



</FieldTypes>




Save the file. 



We are basically done. Notice that RenderingTemplate that was used in previous versions is not included.  We will now create the template to render the item in lists.



Right click on the XSL mapped folder and select add.  We want to create an XSLT file that matches the name of the xml file, just with the XSL extension. fldtypes_Custom.WebPartsList.xsl”



image



In the file add the following:





<?xml version="1.0" encoding="utf-8"?>



<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema"



                xmlns:d="http://schemas.microsoft.com/sharepoint/dsp"



                version="1.0"



                exclude-result-prefixes="xsl msxsl ddwrt"



                xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"



                xmlns:asp="http://schemas.microsoft.com/ASPNET/20"



                xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer"



                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"



                xmlns:msxsl="urn:schemas-microsoft-com:xslt"



                xmlns:SharePoint="Microsoft.SharePoint.WebControls"



                xmlns:ddwrt2="urn:frontpage:internal">



  <xsl:template match="FieldRef[@Name = 'WebPart']" mode="Text_body">



    <xsl:param name="thisNode" select="." />



    <span class="customWebPartID">



      <xsl:value-of select="$thisNode/@*[name()=current()/@Name]" />



    </span>



  </xsl:template>



</xsl:stylesheet>




You may notice that I have included the class=”customWebPartID”, we will use this in our jQuery to replace the ID that would normally be displayed with the WebPart Title.



Now, let’s go ahead and deploy. Right click on the project and select deploy.



After it has completed, let’s create a list, and assign our field.  It is important to note that the Field added to the list must match the FieldRed[@Name = ‘’] in the XSL above to have it render properly.



image



And let’s add our field.



image



Notice the name of the Field matches the XSL and the new column is shown, this value displayed comes from the TypeShortDescription in the field definition XML file. 



I am going to use the title to store the key, and I am going to create another column for the value of the property. 



Now let’s add an item.



image



image



Obviously there is one issue here, and that is this will only work for your own web parts, but you get the idea.  Now hit save.  You can now see the item you created in your list.



image 



If you inspect the HTML, you will see that the value is contained within a span with out css class defined.



image



So at this point, we have a custom column, where we only store the ID, and in the edit and display forms we show the web part title.  The next step is add  a little jQuery.  Check out my next post to find out how.

Wednesday, May 5, 2010

Migrate to your RTM version of SharePoint 2010

Got the beta? Got some important data on it?  I have heard some rumors of an upgrade, but had also heard that MS was not going to support an upgrade from Beta to RTM.  Our team has an internal SP2010 Beta site up and going and I didn’t want to lose everything. 

We only have 2 web applications and about 15 site collections, so I tried a stsadm backup and restore first.  What do you know… it worked.  So then I backed up and restored the rest of them.  All in working order.  I also recompiled some customizations I was playing with, and poof.  Like magic, they deployed without incident, and everything is back up and going.

I found it strange that everything worked the first time, and well, happily surprised!

Here is the command I used:

stsadm –o backup –url http://mysite.myhost.com –filename myhost.bak

then on the new server

stsadm –o restore –url http://mysite.myhost.com –filename myhost.bak

I only created the web applications and not the site collections, although if you have already, you can supply the –override switch.

Happy migrations.

Thursday, April 22, 2010

Using the SharePoint 2010 Object Model to import Twitter Data

As a continuation from my first post about my talk at SharePoint Saturday Atlanta 2010, this part will focus on using the .Net Client Object Model to import data into SharePoint 2010.  This is a great way to avoid the BCS/Enterprise features if you have just a Foundation site. 

Here is a high level of things about the Client Object model:

  • Provided for Silverlight, ECMA/Javascript, and .Net
  • Lightweight server version (limited objects/less property objects within a Site Collection and down)
  • Built into SharePoint Foundation, so available for all of the WSS users out there.
  • Develop solutions off of the server, both in Visual Studio, and while running, the solutions do not need to be on the server
  • Following point #3, no deployment.
  • Connect to the Site via the URL
  • Batch queries, and limited included properties means less chatter and less data transfer on requests.
  • Manage installed Site Collection and Site features
  • Manage most Site Collection and Site properties
  • Manage web parts and pages
  • Manage Site Collection and Site Security

The list goes on…

Now there are some limitations too:

  • Can only work within a Site Collection
  • Cannot create Web Applications or Site Collections
  • Cannot interact with SharePoint Service Applications
  • Cannot install or deploy solutions
  • Cannot install features

For the SharePoint Saturday presentation, I focused on the .Net Client Object Model specifically. 

You can find the .Net Client Object model dlls in the 14 hive ISAPI folder (\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI)

Microsoft.SharePoint.Client.dll
Microsoft.SharePoint.Client.Runtime.dll

Drop them into your VS project as references and add the using statement for the namespace “Microsoft.SharePoint.Client” and you are ready to code.

image

Pass in the Url to the ClientContext constructor to setup your object.  As seen above, I am going to use the Web (Just like a little brother of SPWeb).  Something to note, you cannot interact with the properties of your objects until you can called Load from the Context, and also ExecuteQuery.  “Load” simply adds the call to fill the object to the batch, then ExecuteQuery actually fills out the object details.

Next, I am going to test the list to check and make sure it was not empty. 
image

As long as I have the list, I am going to query it for items.  When working in the Client Object model, it is better to trim down the items you return with a CAML query (since it filters server side, and transfers less data), but in my case, I want all of the list items back.  There is a CreateAllItemsQuery() method, that does just that for me. 

image

Then just pass your query into your list… and call Context.Load() to batch up the request.  Here I have restricted the fields I am going to return from the list to save data in the items being returned.  Since I am only going to check the TweetId value of items before I insert new Tweet data into the list, I have only selected TweetId and ID.

image

I go ahead and call ExecuteQuery() to fill my listItems collection.  Next, I am going to drop the list items returned into a List object of the (SP)ListItem.  Since I plan to create new items, I new up a ListItemCreationInformation object.  This object, along with a handfull of others like it, allow for creation of new Webs, Lists, ListItems, Content Types, Fields, etc…  Finally, I am going to keep a count of new items I am adding, just to show for a sanity check and something to show during the demo.

image

Finally, I am going to run through the items I have, check to see if I can find the TweetId, and if not, add the Tweet to the List.

image

I add some values to the fields that exist on the list, and call listItem.Update().  This method does not commit the change immediately, rather batches up the inserts/changes until we call ExecuteQuery() again.

image

Execute the push back to SP, and comment to the console, just to let me know how many things went in, from the items I got from Twitter.  This whole class is wrapped into a single DLL for my purpose, and I call it from a console application that can run on a Windows scheduled job, takes parameters for SiteUrl, Twitter Search Term, etc.  This was also used in a Windows forms application to show the insert of data.

Finally, when you start working with this off the server, and happen to not be on the domain… well what happens?

Luckily, the other end of the wire is basically a WCF endpoint, so you only need to work with it like you have used web services in the past.

After you create the context object, set the ClientAuthenticationMode to Anonymous, Default (windows), or Forms.  And set the credentials to our old friend the NetworkCredential. (This was my case for a windows domain.)image

Compile everything up, make sure to drop everything into try-catch blocks, and you now have Twitter data in your list… if you have not created your list yet, you can create a list from the custom template, just add the columns listed in the code above, and it should work just fine.  Alternatively, you could create the list using the Client Object model as well!  Just check for the list, if not there, create it in code, then connect to it.

Another alternative is post #3 which will come out tomorrow or Friday, in which I cover the last part of my talk, BCS .Net connector for the Twitter data.

Monday, April 19, 2010

Reading Twitter Search Data in .Net

For the recent SharePoint Saturday Atlanta event, I spoke about consuming Twitter data in .Net to push into SharePoint 2010 via the .Net Client Object Model.  Twitter search has evolved to use a REST web service call which produces either JSON or ATOM.  Since I had not yet used JSON anywhere, I decided to take the bullet and learn it, or rather at least understand it.

JSON is short for JavaScript Object Notation, all the information you might need to know can be found at http://json.org/.

Basically it is lightweight and constrains to a standard, and is being used even by the Client Object model itself.

The Twitter search term we used was #SPTwitter.  We had colleagues of ours post items with the hash tag and turn on geolocation.  Twitter Search API documentation can be found at http://apiwiki.twitter.com/Twitter-Search-API-Method%3A-search.  When requesting the term, I used q for the query and rpp (requests per page) set to 100 to return a max of 100 results.  I could use page to get more results, but 100 was more than enough to show on the Bing Maps/Silverlight example my co-worker was giving in his talk.

Code? Yep, here is what I used to query the REST service:

image

Now we have the JSON response in a string, and we can parse it into our .Net object.  Wait, we need a .Net object AND something to de-serialize the JSON.

There are quite a few options that we could use and after working with a few of them I decided on JSON.Net.  It was pretty straight forward, and is still being maintained.

JSON.Net - http://json.codeplex.com/

So I created a Tweet Class and a Container TweetCollection Class.  The search results are returned in a collection, so we will have to also add some attributes to that class.

image

Notice the JsonProperty Attribute for “results”.  This matches the collection of return results.  Here is a sample:

image

From this example, we can see that the results collection will have individual Tweet/Status data.

So I started with a simple class, Tweet.  I added the Profile Image Url and from_user fields to test it out.

image

Now that we have a simple class, and a collection for it, time to test the de-serialization.

image

The code above fills our collection and objects with real Twitter data!  Not so hard, well, at least someone wrote the parser for us!

Next step was simple enough, just fill the rest of our object with the other data from Twitter.  What about the GeoLocation data?  It is stored in a format like the following: "geo":{"coordinates":[33.9154,-84.3518],"type":"Point"}… but can also be listed as null (see example data above).  So the object geo, contains an array (coordinates) of two doubles and a key value pair for the “type”.

This turned out to be a little more complex than I thought… I followed the same pattern, geo, contained coordiates, and the type.  So I created a Geo class, with coordinates and the type.

image

continued…

image 

One thing to notice is that the type of coordinates did not serialize correctly.  This type being null sometimes also provided some problems.  I did a little research, and determined that a JsonConverter should work for what we needed.  I created the following, using the JsonConverter abstract class, extracting the methods… ReadJson, WriteJson (we are not serializing it back into JSON, so I left this empty, and finally CanConvert.

image

Wrap that into a console or Windows app, and check your console!

Check back tomorrow for part 2, writing to SP using the Object Model.