Saturday, December 4, 2010

SharePoint 2010: Using Visual Studio 2010 to Create a List Definition and List Instance (Based on a new Content Type)

In order to programmatically add a new list definition and instance based on a new content type, you will need to expand upon the solution/project created in the content type post. Right-click the project and select Add New Item. The Add New Item dialog appears:

Select List Definition from Content Type, enter a name, and then click Add. The SharePoint Customization Wizard appears:


Enter an appropriate display name for the list definition and select the content type from the drop-down. The drop-down will only show the content types that are being defined within the current solution. Leave the Add a list instance checked if you want the deployment of the solution to create the actual list. Click Finish.
The list definition elements file is opened. Add Unique=”TRUE”. The Unique setting means that the list instance will be created once and there is no need to show this definition to the users moving forward:

Expand the ListInstance1 item in the Solution Explorer and click on the Elements.xml underneath. This defines the instance of the list (or document library). Change the title to determine the name of the list instance. If the list will be a document library, modify the URL setting by removing the “Lists/”. Enter a name without spaces for the URL. Enter an appropriate description:

Save all of the changes to the files.

Right-click the Project in Solution Explorer and select Deploy. The content type package is built and added to the SharePoint farm solutions. The solution is deployed globally as a feature that is available at the site level. The site feature will be activated at the root site collection but may not be activated within other sites. The site columns, content type, and list (document library) are created accordingly on the root site.


If you found this useful, please help support my SharePoint and .NET user group (Philly SNUG) by clicking on the logo below.

Friday, November 26, 2010

InfoPath Holiday Poem

InfoPath with SharePoint 2010 Holiday Poem

Twas the night before deployment, when all through the farm,
Not a service was stirring, not even an alarm*.
The forms were all published to a network location with care,
In hopes that the Admin will map a drive there.

The secondary data sources were all snug in their connection,
While the Secure Store Service provided protection.
And the assembly all built, and placed in the cache,
Had just settled strong names with keys to match.

When out on the farm there arose such a clatter,
I sprang from the VM to see what was the matter.
Away to the Windows Server, I remoted like a flash,
Tore open the Services and threw up the GAC.

The XML in the manifest and the method it knows,
Gave access to instantiate the objects below.
When, what did my wondering eyes see in the tools,
But picker controls and several “If” rules.

With a little rule driver, so lively and gay,
I knew in a moment it was hacked from the SDK.
More rapid than eagles the Conditions they came,
With dialogs and Actions that called them by name!

“Is Blank! Is Not Equal To! Is not a URL!”,
Setting a field’s value was all blown to hell.
“Begins with! Contains! This Field Changes!”,
Submit will not work with values in these ranges.

As workflows that before the wild tasks did fly,
When they meet with an obstacle,  we just mount a new drive.
So up to the production server they flew,
With an XSN full of schemas, and a DLL too.

And then, in a twinkling, I heard on the prompt,
s-t-s-a-d-m someone did stomp.
I tried to be quiet, I tried not to yell,
“hey, now you can do that using PowerShell…”.


We added the template and with the .wsp,
Deployed the solution at a quarter-til three.
They asked if in a dilemma, what should they do?
Just pick up a copy of “InfoPath with SharePoint 2010 How-To”**!

Happy Holidays!!!

Steve Mann



*another word for alert since alert doesn’t rhyme with farm

**Release Dates
Europe – Dec 3rd
USA and Japan– December 13th
Canada – December 14th

Thursday, November 11, 2010

SharePoint 2010: Leveraging Visual Studio 2010 to Deploy Custom Content Types

lIt's fairly easy to create content types right within SharePoint itself. However, when in a development scenario, you need an easy way to replicate custom content type creation without having to manually enter site columns and site content types by hand. Luckily Visual Studio 2010 has a project for that. Generating  a Content Type project will easily package up site column and content type definitions into a re-distributable solution/feature allowing for seemless deployment between environments (e.g. dev, staging, uat, production, etc.).

The first step is to fire up Visual Studio 2010 and select File -> New -> Project. The New Project dialog appears:


In the New Project dialog  change the target platform at the top to .NET Framework 3.5.and select the Content Type template. Enter the appropriate name and location and click OK. The SharePoint Customization Wizard dialog appears:

The local site should be populated automatically but you may enter a different address if needed. Clicking on the Validate button will ensure the server is reachable. Change the trust level option to Deploy as a farm solution and click Next.


Select the type of base content type that your new content type should inherit. For document libraries, select Document, for lists, select Item. Click Finish.  The project is generated and displays the Elements.xml contents.
TIP: The Document content type actually inherits off of the Item content type. You may use the same type of hierarchical structure if you have a common custom content type that can act as a base for additional custom content types.
The content type structure is displayed but there are no fields. For each custom field you wish to create for the content type, you need a Field Definition. Therefore at the top of the Elements.xml  before the Parent Content Type comment but after the <Elements> tag, enter a field definition entry similar to the following:
<!-- Site Columns -->
<Field ID="{1B0D25D8-5A89-45C0-AE25-FA6726063EB2}" Name="CustomField1" DisplayName="Custom Field 1" Type="Text" Required="FALSE" Group="Custom Document Columns"/>
<Field ID="{3EE19A29-D064-4188-BFA1-BC23968BF1BC}" Name="CustomField2" DisplayName="Custom Field 2" Type="Text" Required="FALSE" Group="Custom Document Columns"/>

To create the ID, use the Create GUID utility which is accessible from the Tools menu. Select the Registry Format (option 4) and click the Copy button. Paste the GUID into the ID. Click the New GUID button for all subsequent columns (field defs). The Elements.xml should look similar to the following:


NOTE: The use of site columns is necessary!! Generating the fields as site columns allows you to use them in other content types.


The current state of the Elements.xml will generate the site columns but not place them into the content type. For that to happen, you need to enter Field Reference entries based on the site column definitions:

<FieldRef ID="{1B0D25D8-5A89-45C0-AE25-FA6726063EB2}" Name="CustomField1" DisplayName="Custom Field 1" />
<FieldRef ID="{3EE19A29-D064-4188-BFA1-BC23968BF1BC}" Name="CustomField2" DisplayName="Custom Field 2" />
     
Place these field reference lines within the <FieldRefs></FieldRefs>  tags. Your content type section should look like the following:


Rename the Content Type to a more meaningful descriptive name. Use the same name to rename the Content Type object in the project within the Solution Explorer pane as well as within the Project-level properties. Save the Elements.xml changes.

Double-click the Feature1 entry in the Solution Explorer pane and modify the Title and Description accordingly as well as the Feature1 item. Save the Feature1.feature file. Double-click the  Package.package file and change the name of the solution file. Save the Package.package file.

If you have a new list or library that will use the new content type you may want to include that within this solution such that they can be deployed together (see this post).

Right-click the Project in Solution Explorer and select Deploy:



The content type package is built and added to the SharePoint site as a farm solution. The solution is deployed globally as a feature that is available at the site level. The site feature will be activated at the root site collection but may not be activated within other sites. The site columns and content type are created accordingly on the root site.

Wednesday, November 10, 2010

BPOS: Adding Your Own Domain

I just posted an article on the RDA Collaboaration Blog in regards to adding a domain to your Microsoft Online Services subscription. It is located here.

BPOS: Setting up Email on a Blackberry Device

I recently have been working with Microsoft Online Services since it provides a viable solution for my friend's up-and-coming campaign for a certain elected position. When signing up for BPOS or one of the other online services, you need to select a domain. The resultant final domain becomes <domain entered>.microsoftonline.com.

Since we didn't want the user's email addresses to be so long like that, we added our own domain which we registered for public website purposes. I have the steps posted on the RDA Collaboration Blog here.
Anyway, after setting up the Exchange Online Services, my friend wanted to access the email from his Blackberry device which looks like it runs a flavor of Outlook. The interface, instructions, and messages were a bit confusing or misleading and it took several tries to enter the right information in the right locations to add the email to his device.

For example purposes, let's say the domain that we added was named "voteformyfriend.com". When I added the users, even though it requires a first and last name, I only used their first name as the user name (per my friend's request). So a valid email for this Exchange server would be steve@voteformyfriend.com. A temporary password is assigned and when the user logs into MOS for the first time, they are prompted to change it.

So the first step was to click on Add a New Email on the Blackberry. You are prompted to use POP/SMTP or Outlook Web Access (OWA). Select the the OWA option and hit enter. That renders a screen asking for a Username and Password. For this prompt enter the email address for the username and then the email password. There is a Show Password button on the Blackberry screen which will umask the entered values - highly recommended! - with the need for upper case and numbers it is real easy to type the wrong password with those micron-sized keys.

The request is processed and the first misleading error message is presented. The messages states something like "The username and/or password are wrong. blah blah blah...or additional information is required". My friend thought it was the wrong password but it wasn't. In reality additional information was required. Those words are a link that you need to click such that you can enter the additional information.

When the additional information page comes up, you are asked for the OWA address. Here we go. There were several iterations of trial and error to figure out which URL to use. The important URL listings for OWA within BPOS are posted here.

There is a seperate section for OWA Access and Mobile access URLs. The prompt asks for the OWA Server URL but it is a Mobile device. So which one to use? I think both will actually work as if you type the OWA address in a broswer it resolves the Mobile address (adds the Redmond server prefix e.g. red001), however, I used the Mobile device listing from that list making sure to use the https:// and not http://.

Now, the screen also prompts for a User Name. Now since we were in the Exchange settings I was thinking that this was the first and last name of the user I created in BPOS or even just the first name. Nope! It is still the email address (e.g. steve@voteformyfriend.com). The example on the screen domain/lisa.perry really makes you think it's the user you setup - but if you think about AD names and such it makes sense - but my friend is not technical..

Next, it asks for a mail box name. Oh boy. In Outlook and OWA on the laptop, the mailbox is showing the full name - so is that the mailbox or is just the username in front of the "@"? After doing it wrong the first few times, it is indeed just the userrname in front of the "@" in the email address (e.g. steve). The example on the screen does elude to this.

Entering this information correctly allowed us to easily setup the email account on his Blackberry device. It does take up to 20 minutes before the email starts sync'ing properly. Hopefully this provides some claification and saves someone some time. We searched online for the correct values to use but with no luck.

If you found this useful, please help support my SharePoint and .NET user group (Philly SNUG) by clicking on the logo below.

Tuesday, November 9, 2010

SharePoint 2010: Patch Warnings in Health Reports

I recently recieved two warnings in Central Admin in regards to upgrades:

  • Product / patch installation or server upgrade required
  • Databases running in compatibility range, upgrade recommended.

I thought this was strange since I just applied the August 2010 Cumulative Hotfixes. After reviewing the details of the issues I realized that the SharePoint Product Configuration wizard needed to be run. The details explain about running psconfig or psconfigUI - don't be alarmed - psconfigUI is just a fancy technical term for the SharePoint Product Configuration wizard. Usually patches and updates run that automatically but I guess I missed the memo that you need to make sure it runs after applying the hotfixes. Anyway, it is an easy fix and I am always confident that the wizard is doing the right thing.
  

Tuesday, October 5, 2010

Microsoft SharePoint Server 2010 Bible Release

While online retailers have been shipping the book since last week, today is the official release of this awesome publication.

Saturday, September 25, 2010

InfoPath 2010: The Infamous Form Name

Yes the infamous form name issue. I rant on about this in my InfoPath Book. I always had an out with the dilemma of new versus updates and keeping the same name.



Several members of my audience at SharePoint Saturday Baltimore eluded to a good solution and one of my colleagues came up with the same idea. It's not anything new but a good altenative if the fields you decide to use can be edited and changed.


The first part of this requires a common theme in form template design; determining if the form is a new instance. I have used a IsNew-type field in the past where the default is 0 or false and when the form instance is rendered (or submitted), rules force it to 1 or true.


So for the form name, instead of using a function at the time of submission, use a field value that has been pre-calculated. If the form is new, create the form name and store that into a field. Then, use that field as the form name that is saved during submission.



This way, the form name will never change during edits. So using an entered field, todays date, etc. in the form name will never have the chance to change since the form name is initially figuired out when the form is new.



Monday, September 13, 2010

SP2010 Patches

The August 31, 2010 patches for SharePoint Foundation and SharePoint Server can be found at the following links:


http://support.microsoft.com/kb/2266423


http://support.microsoft.com/kb/2352342

Upgrade Strategies for SharePoint 2010

If you missed the Quest webcast on SharePoint 2010 migrations (featuring Joel Oleson) , I highly recommend viewing the recorded session which can be found here.

A few main points:

  • Testing, testing, testing! Even if you have gone through dozens of migrations from 2007 to 2010, each farm is different and you should apply the proper due diligence to test the migrations prior to performing the actual upgrade.
  • The MOSS 2007 farm must be SP2 with the latest CU paks with the CU from December being recommended.
  • SP2+ on MOSS 2007 makes SharePoint aware of the database in read-only mode. Therefore, you can make the content databases of your MOSS 2007 farm read-only and SharePoint will automatically hide any Edit, Add, or Delete operations from its menus.
  • Making your databases read-only is a good way to migrate the data since users cannot change anything while you are upgrading. However, the content databases that are being migrated cannot be read-only so you will need to make a copy before attempting DB-Attach operations.
  • The migration check in MOSS 2007 is the preupgradecheck in stsadm.
  • The migration check for SharePoint 2010 is the PowerShell command TEST-SPContentDatabase
  • In-Place migration methods seem to handle most farms, however, it is not recommended as if anything goes wrong, there is no roll back. Your MOSS 2007 farm will be down until all issues can be resolved.

InfoPath 2007/2010/2013: Populate a Repeating Table from a Secondary Data Source

Psst! I have a new nested solution posted here.

Background
In attempting to create a solution within form code to satisfy the scenerio/requirements below, I found several different posts/links that described certain functions. Each link only provided a piece of the puzzle. This post puts all the pieces together.


Scenario/Requirements
Clicking a button or changing a selection in an InfoPath form needs to query a web service (or any secondary data source) based on that selection and populate a repeating table with the data. If the selection changes, the repeating table entries need to be cleared and re-populated with the new set of data.


Assumptions
The querying of the secondary data source (such as a web service) based on selections is already in place (this can be easily done with rules). We just need the proper form code within the control change-event to handle the repeating table population.



Solution Piece-by-Piece

Namespace Variable
Within the changed event form code (using Visual Studio for Office Applications) the first thing we need is the standard namespace variable so we can use that throughout:

string myNamespace = NamespaceManager.LookupNamespace("my");

Access Web Service-Based Secondary Data Source
There is a MainDataSource object at the form level but how can you access secondary data sources? If the datasource is named "GetData" here is the code to access and setup the XPath objects for looping:

DataSource ds = DataSources["GetData"];
XPathNavigator domNav = ds.CreateNavigator();
XPathNodeIterator rows = domNav.Select("/dfs:myFields/dfs:dataFields/tns:GetDataResponse/tns:GetData/NewDataSet/DynamicData", NamespaceManager);

Looping Through the Secondary Data Source
You can loop through the XPathNodeIterator collection using a while-loop. The source fields data can be retrieved by using the Current pointer within the XPathNodeIterator:

while (rows.MoveNext())
{
string accountID = rows.Current.SelectSingleNode("AccountID",NamespaceManager).Value.ToString();
string accountNumber = rows.Current.SelectSingleNode("AccountNumber",NamespaceManager).Value.ToString();
string amount = rows.Current.SelectSingleNode("Amount",
NamespaceManager).Value.ToString
}

Populating the Repeating Table
The repeating table is actually part of the main data source so we can access that and use the XMLWriter to write the field values from the web service to the table. The code below will live within the loop from above:

using (XmlWriter writer = MainDataSource.CreateNavigator().SelectSingleNode("/my:MainDataSource/my:group1", NamespaceManager).AppendChild())
{


writer.WriteStartElement("group2", myNamespace);
writer.WriteElementString("Amount",myNamespace ,amount);
writer.WriteElementString("AccountID", myNamespace, accountID);

writer.WriteElementString("AccountNumber", myNamespace,
accountNumber);

writer.WriteEndElement();
writer.Close();
}

The above assumes the repeating table created a Group1\Group2 data source entry. You must look at the main datasource to determine the group names.

ALSO! VERY IMPORTANT! The order in which you write the values to the table should be the order that they appear in the main data source. So if you expand the group1 and group2 and see field1, field2, field3, that is the order you must have in the code above. Otherwise you will receive a non-datatype schema validation error.


Clearing Previous Entries
Before anything happens, we need to check if there are already rows in the repeating table and remove them. This must be done by removing the rows in a descending fashion because the count actually represents the highest index.

First we check to see if there are any rows in the repeating table and if so we loop through and delete them. Notice how the actual row is accessed using the SelectSingleNode (SelectSingleNode("/my:MainDataSource/my:group1/my:group2[row#]):

XPathNavigator rTable = MainDataSource.CreateNavigator();
XPathNodeIterator tableRows = rTable.Select("/my:MainDataSource/my:group1/my:group2", NamespaceManager);
if (tableRows.Count > 0)
{


for (int i = tableRows.Count;i > 0; i--)
{
XPathNavigator reTable =
MainDataSource.CreateNavigator();

XPathNavigator reTableRows =
reTable.SelectSingleNode("/my:MainDataSource/my:group1/my:group2[" + i + "]", NamespaceManager);

reTableRows.DeleteSelf();
}
}




Solution - Complete Code
//Get namespace
string myNamespace = NamespaceManager.LookupNamespace("my");
//Clear any previous entries
XPathNavigator rTable = MainDataSource.CreateNavigator(); XPathNodeIterator tableRows = rTable.Select("/my:MainDataSource/my:group1/my:group2", NamespaceManager);

if (tableRows.Count > 0)
{
for (int i = tableRows.Count;i > 0; i--)
{
XPathNavigator reTable =
MainDataSource.CreateNavigator();
XPathNavigator reTableRows =
reTable.SelectSingleNode("/my:MainDataSource/my:group1/my:group2[" + i + "]",
NamespaceManager);

reTableRows.DeleteSelf();
}
}
//Connect to secondary data source
DataSource ds = DataSources["GetData"];
XPathNavigator domNav = ds.CreateNavigator(); XPathNodeIterator rows = domNav.Select("/dfs:myFields/dfs:dataFields/tns:GetDataResponse/tns:GetData/NewDataSet/DynamicData", NamespaceManager);

//Loop through secondary data source and populate the repeating table

while (rows.MoveNext())
{

string accountID =
rows.Current.SelectSingleNode("AccountID", NamespaceManager).Value.ToString();

string accountNumber =
rows.Current.SelectSingleNode("AccountNumber",NamespaceManager).Value.ToString();

string amount =
rows.Current.SelectSingleNode("Amount", NamespaceManager).Value.ToString();

using (XmlWriter writer =
MainDataSource.CreateNavigator().SelectSingleNode("/my:MainDataSource/my:group1",
NamespaceManager).AppendChild())


{
writer.WriteStartElement("group2", myNamespace);
writer.WriteElementString("Amount",myNamespace ,amount);
writer.WriteElementString("AccountID",
myNamespace, accountID);

writer.WriteElementString("AccountNumber", myNamespace,
accountNumber);

writer.WriteEndElement();
writer.Close(); }

}


WHAT ABOUT A NESTED REPEATING TABLE? THE ANSWER IS HERE!

You can download a sample form and project code with the purchase of my bestselling InfoPath book:



The Nested Solution will be part of my new InfoPath with SharePoint 2013 book download.