Friday, March 25, 2011

Simple Way of Creating a SharePoint 2010 Custom List Definition

There are several ways to create SharePoint custom list definitions and each have their own advantages and disadvantages. But there is a common difficulty in most of these methods; if we make a mistake in manually creating the Schema file, it is sometimes very difficult to troubleshoot the error. The method explained in this post is a UI based way of creating custom list definitions, which can help in this scenario. These are the steps.

  • Create a custom list
  • Save it as a Template
  • Do few modifications to the template file to get the list definition
  • Add modified list definition to a new SharePoint list definition

Create a Custom List:

1. Go to “Site Settings” - > “View All Site Content” and create a custom list.

clip_image002

2. Use “Create Column” option to add required columns.

clip_image003

3. Use “Create View” option to create any views required.

4. This is the sample list I created.

clip_image004

 

Save List as a Template

1. Go to “List Settings” and select "Save list as template", under "Permissions and Management".

2. Provide a "File name" and a "Template name" and save the list template. Template will be saved in “List template gallery”.

clip_image005

3. Download the template file which is with .stp extension and extract it using Win RAR.

4. We can find a XML Document called "manifest.xml" inside.

5. In the manifest.xml file, there is a section called “UserLists” and within that there is a “List” section which is the actual sechema.xml that we need to have in a custom list definition.

clip_image007

 

Modifications Required to the Template

1. Delete other sections except for the “XML version” and the “List” section.

clip_image008

2. Change the “Name” and “Url” attributes of the List, as preferred.

<List Name="My Custom List" Title="My Custom List" Url="Lists/My List" FeatureId="{00BFEA71-DE22-43B2-A848-C05709900100}" ...>

3. Find the “Views” section and add WebPartZoneID="Main", SetupPath="pages\viewpage.aspx" attributes to the each View. Change Url attribute to Url="AllItems.aspx".

<View Name="{352071D0-7C1E-4AD6-8875-8A11125CCBCF}" BaseViewID="1" WebPartZoneID="Main" SetupPath="pages\viewpage.aspx" Url="AllItems.aspx" ...>

4. Find the “Forms” section and replace three inner “Form” tags with the following.

<Form Type="DisplayForm" Url="DispForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />

<Form Type="EditForm" Url="EditForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />

<Form Type="NewForm" Url="NewForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />

 

Add modified List definition to a new SharePoint List definition

1. Use Add New window of Visual Studio and add a Custom List type List Definition.

clip_image009

2. Find the Schema.xml file of the new list definition; replace body with our modified list definition.

3. Find respective feature of the new list definition and get the FeatureId.

clip_image010

4. Go to the list definition in the Schema.xml. Update FeatureId attribute of the “List” tag with this FeatureId.

5. That’s it and the final schema file should look like as follows.

clip_image011

Wednesday, March 23, 2011

Customize Entity Framework Code Generation with T4 Templates

The ADO.NET Entity Framework enables developers to create data access applications by programming against a conceptual application model. In here the logical schema and its mapping with the physical schema is represented by an Entity Data Model (EDM). We can design our Entity Data Model by using Entity Data Model Designer or updating from a database. In both ways it auto generates the relevant code (ObjectContext and various EntityObject classes) in Model.designer.cs file and manual changes to this file may cause unexpected behavior in the application. Also manual changes will be overwritten if the code is regenerated.

But what if we want to do a change to the way file auto-generates? For example in order to XML serialize objects with populated property objects, you might want to remove [XmlIgnoreAttribute()] attribute from the generated code.

Here we can use T4 (Text Template Transformation Toolkit) templates to customize how the ObjectContext and entity classes are created. For the purpose we start with the default T4 Template and then modify it.

This is my simple model:



If we look at the auto generated code, we can see “[XmlIgnoreAttribute()]” as in the following code.

///
/// No Metadata Documentation available.
///

[XmlIgnoreAttribute()]
[SoapIgnoreAttribute()]
[DataMemberAttribute()]
[EdmRelationshipNavigationPropertyAttribute("TestT4Model", "FK_Region_RegionType", "Region")]
public EntityCollection Regions
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection("TestT4Model.FK_Region_RegionType", "Region");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection("TestT4Model.FK_Region_RegionType", "Region", value);
}
}
}


Add a T4 Template to the Model:
We can add a T4 Template by Right Click -> Add Code Generation Item… or



By using Add New Item window and selecting ADO.NET EntityObject Generator type.


Then we will get a new item with the .tt extension. Now if we examine Model1.Designer.as file, we can see it is empty. Instead Model1.cs file under Model1.tt file has that generated code.



Now to change the code generation we need to modify Model1.tt template. If we open the Model1.tt file, we can see text which is inside <# #> and text which is not inside <# #> brackets. The text which is inside <# #> is the code to be executed. Text which is not inside the angle brackets will simply be written out to the file. Here is a part of the Model1.tt content.

<# //////// //////// Write Navigation Properties. //////// region.Begin(GetResourceString("Template_RegionNavigationProperties")); foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(n => n.DeclaringType == entity))
{
VerifyGetterAndSetterAccessibilityCompatability(navProperty);
#>

///
/// <#=SummaryComment(navProperty)#>
///
<#=LongDescriptionCommentElement(navProperty, region.CurrentIndentLevel)#>
[XmlIgnoreAttribute()]
[SoapIgnoreAttribute()]
[DataMemberAttribute()]
[EdmRelationshipNavigationPropertyAttribute("<#=navProperty.RelationshipType.NamespaceName#>", "<#=navProperty.RelationshipType.Name#>", "<#=navProperty.ToEndMember.Name#>")]
<# if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) { #>

Here if we search for “[XmlIgnoreAttribute()]” attribute we can find it outside the <# #> brackets. Which means it will simply be written out to the file. So I can find and delete those “[XmlIgnoreAttribute()]” attributes where necessary and modified file will be like follows.

<# //////// //////// Write Navigation Properties. //////// region.Begin(GetResourceString("Template_RegionNavigationProperties")); foreach (NavigationProperty navProperty in entity.NavigationProperties.Where(n => n.DeclaringType == entity))
{
VerifyGetterAndSetterAccessibilityCompatability(navProperty);
#>

///
/// <#=SummaryComment(navProperty)#>
///
<#=LongDescriptionCommentElement(navProperty, region.CurrentIndentLevel)#>
[SoapIgnoreAttribute()]
[DataMemberAttribute()]
[EdmRelationshipNavigationPropertyAttribute("<#=navProperty.RelationshipType.NamespaceName#>", "<#=navProperty.RelationshipType.Name#>", "<#=navProperty.ToEndMember.Name#>")]
<# if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) { #>

Then when we save this template, code generation will be happen automatically and “[XmlIgnoreAttribute()]” is not there in the generated code in Model1.cs.

///
/// No Metadata Documentation available.
///

[SoapIgnoreAttribute()]
[DataMemberAttribute()]
[EdmRelationshipNavigationPropertyAttribute("TestT4Model", "FK_Region_RegionType", "RegionType")]
public RegionType RegionType
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference("TestT4Model.FK_Region_RegionType", "RegionType").Value;
}
set
{
((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference("TestT4Model.FK_Region_RegionType", "RegionType").Value = value;
}
}

Tuesday, March 22, 2011

Upload image to Picture Library using SharePoint 2010 Web Services

We can use http:///_vti_bin/Copy.asmx Web service to upload files to a SharePoint document library or a picture library from another list. Here we use "GetItem" method to download files from the source list and "CopyIntoItems" method to upload files to the destination list. But there is a limitation that you can't copy between locations that are not on the same Web Application directly.

Following is a workaround for that limitation using two references to the source and destination sites. In this way it is possible to post an image from a SharePoint server located in one domain to another SharePoint server located in a separate domain.

In here “IntranetBlogCopyService” is a Web Reference to the http://moss2010/sites/IntranetBlog/_vti_bin/Copy.asmx service and “InternetBlogCopyService” is a a Web Reference to the ‘http://prasadmoss.domainx.local/sites/InternetBlog/_vti_bin/Copy.asmx” service.


///
/// Initialize Intranet Blog Copy Service.
///

private IntranetBlogCopyService.Copy InitializeIntranetBlogCopyService()
{
IntranetBlogCopyService.Copy intranetBlogCopyService = new IntranetBlogCopyService.Copy();
intranetBlogCopyService.Credentials = CredentialCache.DefaultCredentials;
intranetBlogCopyService.Url = "http://moss2010/sites/IntranetBlog/_vti_bin/Copy.asmx";
return intranetBlogCopyService;
}


///
/// Initialize Internet Blog Copy Service.
///

private InternetBlogCopyService.Copy InitializeInternetBlogCopyService()
{
InternetBlogCopyService.Copy internetBlogCopyService = new InternetBlogCopyService.Copy();
internetBlogCopyService.Credentials = CredentialCache.DefaultCredentials;
internetBlogCopyService.Url = "http://prasadmoss.domainx.local/sites/InternetBlog/_vti_bin/Copy.asmx";
return internetBlogCopyService;
}


///
/// Copy image between two Picture Libraries.
///

private void PostAnImageFromIntranetSiteToInternetSite()
{
IntranetBlogCopyService.Copy intranetBlogCopyService = InitializeIntranetBlogCopyService();
InternetBlogCopyService.Copy internetBlogCopyService = InitializeInternetBlogCopyService();

string sourceAbsoluteUrl = "http://moss2010/sites/InternetBlog/Lists/Photos/image01.png";
string[] destinationAbsoluteUrls = { "http://prasadmoss/sites/InternetBlog/Lists/Photos/image01.png" };

IntranetBlogCopyService.FieldInformation intranetFieldInfo = new IntranetBlogCopyService.FieldInformation();
IntranetBlogCopyService.FieldInformation[] intranetFieldInfoArray = { intranetFieldInfo };
byte[] imageByteArray;
uint getUint = intranetBlogCopyService.GetItem(sourceAbsoluteUrl, out intranetFieldInfoArray, out imageByteArray);

// Create FieldInformation of InternetCopyService type.
InternetBlogCopyService.FieldInformation internetFieldInfo = new InternetBlogCopyService.FieldInformation();

// Copy individual fields.
internetFieldInfo.DisplayName = intranetFieldInfoArray[0].DisplayName;
internetFieldInfo.InternalName = intranetFieldInfoArray[0].InternalName;
internetFieldInfo.Value = intranetFieldInfoArray[0].Value;
internetFieldInfo.Id = intranetFieldInfoArray[0].Id;

InternetBlogCopyService.FieldInformation[] internetFieldInfoArray = { internetFieldInfo };

InternetBlogCopyService.CopyResult copyResult1 = new InternetBlogCopyService.CopyResult();
InternetBlogCopyService.CopyResult[] copyResultArray = { copyResult1 };

try
{
uint myCopyUint = internetBlogCopyService.CopyIntoItems(sourceAbsoluteUrl, destinationAbsoluteUrls, internetFieldInfoArray, imageByteArray, out copyResultArray);
if (myCopyUint == 0)
{
int idx = 0;
foreach (InternetBlogCopyService.CopyResult myCopyResult in copyResultArray)
{
string opString = (idx + 1).ToString();
if (copyResultArray[idx].ErrorMessage == null)
{
// Copy operation success.
}
else
{
// Copy operation failed.
}
idx++;
}
}
}
catch (Exception ex)
{
// Error
}
}

Wednesday, March 9, 2011

IIS URL Rewrite Module - Redirect HTTP to HTTPS with IIS 7

Using IIS URL Rewrite Module, Web administrators can easily set up rules to define URL rewriting behavior based on HTTP headers, HTTP response or request headers, IIS server variables, and programmatic rules. Here the main purpose is to implement URLs that are easier for users to remember and easier for search engines to find.

In addition, we can define rules to force SSL on selected pages of a Website which is hosted in IIS 7. First of all we need to install Microsoft URL Rewrite Module in IIs. Then we need to install SSL certificate, create HTTPS bindings to our IIS Web site and assign the certificate. Then select our Web site under Sites node and make sure "Require SSL" is NOT checked under SSL Settings.

After that we have to add following config section to the web.config file in the Web site root directory.
















In here a rewrite rule is defined by specifying a pattern to use for matching the URL string and an action to perform if a pattern is matched. In addition an optional condition is also specified which will be checked in the matching step.

Thanks Carlos Redondo for your suggestion!

Monday, March 7, 2011

Skill Based Search in Lync 2010

As we search in Lync for people in the organization by name, we can configure it to enable search for people by skill set. When I was searching for ways on extending Lync Client, I came across this built-in feature called “Skill Based Search” on Lync 2010. When we configure Lync Skill Search, users can search SharePoint Server 2010 My Site pages for names, keywords, or specific skills. We can also add a link at the bottom of the Lync search results window that opens the search results in SharePoint.

(My Sites in SharePoint 2010 is a personal site for individual users that gives a central location to manage and store documents, content, newsfeeds, links, and contacts. Also My Site contains a section defining and sharing profile information and content with other users in the organization. My Sites give users rich social networking and collaboration features enabling easily share information about themselves and their work. This article describes how to set up My Sites in Microsoft SharePoint Server 2010.)

In order to configure Lync 2010 Skill Search, "New-CsClientPolicy" or "Set-CsClientPolicy" commands are used with "SPSearchExternalURL" and "SPSearchInternalURL" parameters to configure a Client Policy. Then using "Grant-CsClientPolicy" we can assign the new policy/policies to users.

Also using "SPSearchCenterExternalURL" and "SPSearchCenterInternalURL" parameters we can add a link to the bottom of the Skill Search results that says, “View results in SharePoint…”. Users can use this link to refine search results by using the advanced search capabilities of SharePoint Server.

Link
Commands to run in Power Shell to enable the Skill based search URLs;
Set-CSClientPolicy -SPSearchInternalURL http://<server>/_vti_bin/search.asmx
Set-CSClientPolicy -SPSearchExternalURL http://<server>/_vti_bin/search.asmx
Set-CSClientPolicy -SPSearchCenterInternalURL http://<server>/SearchCenter/Pages/PeopleResults.aspx
Set-CSClientPolicy -SPSearchCenterExternalURL http://<server>/SearchCenter/Pages/PeopleResults.aspx

Parameters;
SPSearchCenterExternalURL: This is for users logging on from outside the organization’s firewall. This URL will appear at the bottom of keyword search results giving the user the opportunity to conduct searches using the search capabilities of SharePoint.
SPSearchCenterInternalURL: URL for users logging on from inside the organization’s firewall to conduct searches using the search capabilities of SharePoint.
SPSearchExternalURL: Lync 2010 will use this SharePoint site when a user who has accessed the system from outside the organization’s firewall conducts a keyword search.
SPSearchInternalURL: Will use the SharePoint site located at this URL when a user who has logged on from inside the organization’s firewall conducts a keyword search.

For more information on Enabling Skill Search in Lync 2010;
http://blogs.catapultsystems.com/tharrington/archive/2010/11/15/enabling-skill-search-in-lync-2010.aspx
For more information on getting this to work externally via Threat Management Gateway;
http://techblurt.com/2011/02/22/lync-2010-people-search-with-sharepoint-2010-and-tmg/
For more information on Lync 2010 Integration;
http://technet.microsoft.com/en-us/library/gg398806.aspx