Saturday, March 13, 2010

How to determine global navigation inheritance for non-publishing sites

Publishing sites are represented in Sharepoint by PublishinWeb class which has several convenient methods and properties for working with navigation (see my previous post about basics of navigation in Sharepoint). One of them is InheritGlobalNavigation property which contains boolean value showing whether publishing site inherits navigation from parent site or it has own navigation items (corresponds to “Display the navigation items below the current site“ option from Site Settings > Navigation).

But you can determine this property also for non-publishing sites represented by regular SPWeb class. Look inside InheritGlobalNavigation property:

   1: public bool InheritGlobalNavigation
   2: {
   3:     get
   4:     {
   5:         if (this.IsRoot)
   6:         {
   7:             return false;
   8:         }
   9:         return this.Web.Navigation.UseShared;
  10:     }
  11:     set
  12:     {
  13:         if (!this.IsRoot)
  14:         {
  15:             this.Web.Navigation.UseShared = value;
  16:         }
  17:     }
  18: }

Internally it reuses SPWeb.Navigation.UseShared property. So it is not necessary to obtain PublishingWeb instance if you only have SPWeb instance by extra call of PublishingWeb.GetPublishingWeb(SPWeb web) method. It is enough to have SPWeb instance and check Navigation.UseShared property.

Monday, March 8, 2010

Ways to copy sites in Sharepoint

Here is summary of possible ways to copy sites in Sharepoint. It can be useful if you make investigation of existing tools. I recommend to read this article which explains several ways to copy sites using Sharepoint Designer and Sharepoint UI commands.

  • Web packages (.fwp files) – see http://office.microsoft.com/en-us/sharepointdesigner/HA100819231033.aspx
    ”Use this feature when you want to share or reuse Web pages or a site, list, or library structure. With Web packages, you can either package an entire site or choose the specific pages, lists, or libraries that you want. Web packages are a great way to duplicate site structure, but they cannot include list data, subsites, or security and permissions settings. Also, a Web package can include custom link bars. After you create a package, you can import and deploy the file into as many Web sites as you want”
  • Content migration packages (.cmp files) – see http://office.microsoft.com/en-us/sharepointdesigner/HA100699391033.aspx
    ”Use this feature when you want to make a backup copy of an entire site or subsite, or when you want to move an entire site or subsite to another server or location. Unlike Web packages, content migration packages include list data, and you cannot choose to include only specific site elements. Note that you may lose some customizations or settings in the process. For example, the backup file does not include workflows, alerts, and properties stored at the site collection level. The backup file also does not include the Recycle Bin state or objects inside it. To use Backup and Restore, you must have administrator permissions to that site”
  • Site templates (.stp files) – see http://office.microsoft.com/en-us/help/HA101577791033.aspx?pid=CL100605171033
    ”Use this feature when you want to create multiple Web sites that start with the same base content or site structure. For example, you may want all of the subsites created on a site to have a similar site structure, look and feel, and even content. You can do this by saving a site as a site template and adding it to the Site Template Gallery so that anyone can use the site as a template. Unlike Web packages, site templates can include list data. You must have both administrator permissions to the Web site where you want to create the site template and write access to Site Template Gallery”
  • Use backup/restore commands of stsadm.exe utility (http://technet.microsoft.com/en-us/library/cc263441.aspx and http://technet.microsoft.com/en-us/library/cc262087.aspx)
    Here you can choose whether you want to backup site collection, database, web application or the entire farm.
  • SPImport/SPExport API
    See great posts of Stefan Goßner for more information. This way however has a known problem that with RetainObjectIdentity = true it works only for sites which are children of root site. In further posts I will describe why it happens and show workaround for this issue
  • Stsadm extension commands from Gary Lapointe
    See details of gl-import2 command

I will update this post as far as I will investigate new ways to copy sites and content in Sharepoint.

Wednesday, March 3, 2010

Sharepoint: IE8 slow down in page edit mode

Recently our customers complained that when they try to edit page with IE8 it takes too much time to proceed the changes. When I tried to reproduce the issue I found that IE8 waits ~ 20-30 seconds and then shows the following javascript warning message during postbacks:

image

“Are you sure you want to navigate away from this page?

The page took too long to save. You can click "Cancel", and then try to save the page again. If you click "OK", you might lose unsaved data.

Press OK to continue, or Cancel to stay on the current page.”

Notice that IE7 works properly – i.e. there are no any warnings and it works fast enough. So 1st I tried to launch the page in Compatibility View by setting “Display all websites in Compatibility View” in Tools > Compatibility View Settings:

image

But it didn’t help – mentioned issue didn’t disappear. Also I tried to change Browser Mode and Document Mode in Developer Tools – and it didn’t work also:

image

During further investigation I found the following blog post: http://www.chakkaradeep.com/post/2009/07/11/IE8-to-SharePoint-e28098Are-you-sure-you-want-to-navigate-away-from-this-pagee28099.aspx. It describes solution which worked for us: in order to fix it you should go to Tools > Internet Options > Advanced and turn off "Enable native XMLHTTP support" option:

image

After this warning disappeared and IE8 started to work as fast as IE7 in edit mode.

The remaining question for me: is why Compatibility View didn’t help in this situation? Seems like Compatibility View doesn’t give compatibility for all features, e.g. native XMLHTTP support still works differently in IE8 even in Compatibility View.

Thursday, February 25, 2010

Login into Sharepoint with old AD password

Recently I encountered with strange issue: users were able to login into Sharepoint with old password after their AD password was changed. Actually they were able to login both with old and new passwords. We use FBA with AD as users storage, i.e. users accounts and passwords are stored in Active Directory. So when user changes his password – he actually changes password in AD.

As membership provider we use OTB LdapMembershipProvider configured like this in web.config:

   1: <membership defaultProvider="fbaMembers">
   2:   <providers>
   3:     <add name="fbaMembers" type="Microsoft.Office.Server.Security.LdapMembershipProvider,
   4: Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C"
   5: server="localhost" port="389" useSSL="false" userDNAttribute="distinguishedName"
   6: userNameAttribute="userPrincipalName" userContainer="..."
   7: userObjectClass="person" userFilter="(|(ObjectCategory=group)(ObjectClass=person))"
   8: scope="Subtree" otherRequiredUserAttributes="sn,givenname,cn" />
   9:   </providers>
  10: </membership>

By1st look at the issue I thought that this is a bug. I tried to restart IIS, reopen browser – but user still was able to login with both passwords. I was sure that user’s password was changed successfully as I checked it with “Run As…” command – program could be run only if I specified new password in “Run As…” credentials window. More strange thing is that after some time old password became unusable, i.e. only new password remains valid for login into Sharepoint site.

After some investigation I found the following KB article which says that this behavior is actually by design:

“Microsoft Windows Server 2003 Service Pack 1 (SP1) modifies NTLM network authentication behavior. After you install Windows Server 2003 SP1, domain users can use their old password to access the network for one hour after the password is changed.

The lifetime period of the old password can be configured by editing the registry on a domain controller. No restart is required for this registry change to take effect

Note The lifetime period is set in minutes. If this registry value is not set, the default lifetime period for an old password is 60 minutes”

I.e. LdapMembershipProvider treats old password as valid password for authentication during 1 hour by default after password was changed.

Sunday, February 21, 2010

The basics of navigation in Sharepoint

With this blog post I introduce several articles about navigation in Sharepoint. Here I would like to describe basic architecture of MOSS navigation. The understanding of these basics is required to customize navigation in Sharepoint.

Important point is that Sharepoint navigation is built on top of ASP.Net navigation. I.e. all that you know about ASP.Net navigation will help you in Sharepoint as well. As you know ASP.Net navigation architecture separates UI and navigation data. It is based on 3 main components:

  • navigation UI controls which display navigation items. In Sharepoint most widely used navigation control is AspMenu control defined in OTB master pages of publishing sites. As said here:

The AspMenu class that ships with WSS 3.0 (and by extension MOSS 2007) is nearly identical in behavior to ASP.NET 2.0’s Menu class (as the name implies). AspMenu derives from Menu and adds tweaks to work around a few reasonably well known annoyances and provides improved highlighting support.

  • navigation data source - intermediate abstraction layer between UI controls and navigation data returned by navigation providers. You can use ASP.Net SiteMapDataSource control or PortalSiteMapDataSource from MOSS API which inherits SiteMapDataSource. The main purpose of these controls – is to add additional configuration abilities for developers (e.g. specify starting node URL, starting offset, etc.) and bind to site map data and presents its view. I.e. SiteMapDataSource returns instance of SiteMapDataSourceView or SiteMapHierarchicalDataSourceView - it’s kind of “view model” for navigation;
  • navigation providers which return navigation data (SiteMapNode instances). In Sharepoint navigation data is stored in content database, so navigation providers fetch navigation nodes from db – this is important point to note. SPSiteMapProvider, SPContentMapProvider, and SPNavigationProvider are part of the WSS classes; PortalSiteMapProvider is part of the MOSS object model.

Here is a figure from msdn which explains how these components are used in OTB master page of publishing site:

schema

Three PortalSiteMapDataSource controls are embedded. Each maps to a definition for a provider within the web.config file of the site. The SiteMapDataSourceRoot control uses the CombinedNavSiteMapProvider control and is connected to the breadcrumb controls. As its name suggests, it combines the two other PortalSiteMapDataSource controls into one. The siteMapDataSource1 control provides the data for the top navigation bar, and the siteMapDS control provides the data for the vertical navigation control

In order to avoid terms mess I would like to notice that “top navigation bar” here is “global navigation” in terms of Sharepoint and “vertical navigation control” is “current navigation”.

So what happens when we change navigation in OTB “Site Navigation Settings” page of the Sharepoint site (Site Actions > Site Settings > Modify All Site Settings > Navigation)?

image

Sharepoint saves changes made on this page in content database. I.e. here you can affect only SiteMapNode instances returned by providers. All visual appearance settings are still available only from page which contains SiteMapDataSource and UI controls (master page). Notice that you can also change navigation settings programmatically using the following members of PublishingWeb class:

   1: public sealed class PublishingWeb
   2: {
   3:     ...
   4:     // Methods
   5:     public void ExcludeFromNavigation(bool useGlobal, Guid item);
   6:     public void IncludeInNavigation(bool useGlobal, Guid item);
   7:  
   8:     // Properties
   9:     public SPNavigationNodeCollection CurrentNavigationNodes { get; }
  10:     public SPNavigationNodeCollection GlobalNavigationNodes { get; }
  11:     public bool IncludeInCurrentNavigation { get; set; }
  12:     public bool IncludeInGlobalNavigation { get; set; }
  13:     public bool IncludePagesInNavigation { get; set; }
  14:     public bool IncludeSubSitesInNavigation { get; set; }
  15:     public bool InheritCurrentNavigation { get; set; }
  16:     public bool InheritGlobalNavigation { get; set; }
  17:     public AutomaticSortingMethod NavigationAutomaticSortingMethod { get; set; }
  18:     public OrderingMethod NavigationOrderingMethod { get; set; }
  19:     public bool NavigationShowSiblings { get; set; }
  20:     public bool NavigationSortAscending { get; set; }
  21:     ...
  22: }

Gary Lapointe implemented 2 stsadm commands which can be used for changing navigation settings using stsadm utility: gl-setnavigationnodes and gl-copynavigation. Internally these commands use the mentioned above members of PublishingWeb class. Details can be found in his blog.

That’s what I wanted to write about basics of Sharepoint navigation. Hope it will help you to increase understanding of internal Sharepoint mechanisms. Based on this knowledge you can create you own custom navigation providers in MOSS. I’m going to write separate post about it.

Update 2010-03-14. Also you can use SPWeb.Navigation property which returns instance of SPNavigation class with the following public interface:

   1: public sealed class SPNavigation
   2: {
   3:     // Fields
   4:     public SPNavigationNode GetNodeById(int id);
   5:     public SPNavigationNode GetNodeByUrl(string url);
   6:  
   7:     public SPNavigationNodeCollection GlobalNodes { get; }
   8:     public SPNavigationNode Home { get; }
   9:     public SPNavigationNodeCollection QuickLaunch { get; }
  10:     public SPNavigationNodeCollection TopNavigationBar { get; }
  11:     public bool UseShared { get; set; }
  12:     public SPWeb Web { get; }
  13: }

Thursday, February 18, 2010

UnauthorizedActionResult for ASP.Net MVC

Here is the simple class which represents action result for not authorized access:

   1: public class UnauthorizedActionResult : ActionResult
   2: {
   3:     public override void ExecuteResult(ControllerContext context)
   4:     {
   5:         context.HttpContext.Response.StatusCode =
   6:               (Int32)HttpStatusCode.Unauthorized;
   7:         context.HttpContext.Response.ContentType = "text/html";
   8:         using (var sw = new StreamWriter(context.HttpContext.Response.OutputStream, Encoding.UTF8))
   9:         {
  10:             using (var tw = new HtmlTextWriter(sw))
  11:             {
  12:                 tw.RenderBeginTag(HtmlTextWriterTag.Html);
  13:                 tw.RenderBeginTag(HtmlTextWriterTag.Head);
  14:                 
  15:                 tw.RenderBeginTag(HtmlTextWriterTag.Title);
  16:                 tw.Write(UnauthorizedActionResources.Titlte);
  17:                 tw.RenderEndTag();
  18:  
  19:                 tw.RenderEndTag();
  20:  
  21:                 tw.RenderBeginTag(HtmlTextWriterTag.Body);
  22:                 tw.Write(UnauthorizedActionResources.Text);
  23:                 tw.RenderEndTag();
  24:  
  25:                 tw.RenderEndTag();
  26:             }
  27:         }
  28:     }
  29: }

It can be used like this in your controller:

   1: public class AdminController : Controller
   2: {
   3:     private IUserSession userSession;
   4:  
   5:     public AdminController(IUserSession userSession)
   6:     {
   7:         this.userSession = userSession;
   8:     }
   9:  
  10:     [HttpGet]
  11:     public ActionResult Panel()
  12:     {
  13:         var user = this.userSession.GetCurrentUser();
  14:         if (user == null || !user.IsAdmin)
  15:         {
  16:             return new UnauthorizedActionResult();
  17:         }
  18:         return View("Panel");
  19:     }
  20: }

If this action result is returned ASP.Net MVC will redirect user on the view which corresponds to loginUrl attribute of forms tag in your web.config.

Also notice that if you will use another http response code (e.g. 403 Forbidden) IE will not show your custom error message if it has size less than 512 bytes – it will show its own “friendly error message”: http://stackoverflow.com/questions/1492444/ie-7-not-showing-my-custom-401-page/1492472.

Saturday, February 13, 2010

Speaking on SPb ALT.Net with overview of database migration tools

On this week (February, 11) I made an overview of database migration and versioning tools on SPb ALT.Net meeting. Materials and details can be found here. There were several tool concerned:

Lets briefly summarize advantages and disadvantages of the mentioned tools.

Tarantino.Net (used in http://code.google.com/p/codecampserver open source ASP.Net MVC project) – uses NHibernate schema export feature and SQL Compare tool for generating update scripts. Advantage of such approach is that developers don’t work directly with SQL when they need to synchronize modified model (expressed in c# code) with db schema as Tarantino will make it automatically. And this is really great feature. But in real world there can be situations when NHibernate schema export don’t provide exactly desired result by default and in this case some efforts should be made to fine tune it (e.g. if return to examples shown on meeting – NHibernate generated nullable column for decimal value type – but more desired behaviour was generating of not-nullable column with default value). Also NHibernate schema export affects those entities which are persisted with NHibernate and if you need to modify schema of table which is not included in ORM (or you use several ORM frameworks in your project) then you will need to do extra efforts also.

Migrator.Net supports code-first approach. Thus you have compile time checking and more control over resulting SQL schema as all migrations are written by developer via c# code. Migrator.Net has a good and intuitive API which helps to write migrations. But with this you will have more manual work as all database assets should be described in code one by one. Also it have some bugs (what program doesn’t?) – if return again to examples on meetings there was little confuse when Migrator.Net failed to downgrade db to version = 0 (version of database without schema) because it couldn’t drop table “Order” because Order is a keyword in SQL (yes we tried “[Order]” also without result :) ). Nevertheless Migrator.Net made an impression on me as nice and intuitive tool.

Subsonic has the same idea as Migrator.Net – it uses migrations written in c# code to change db schema. Subsonic has command line utility sonic.exe to perform migrations (for Tarantino.Net and Migrator.Net I used nant tasks). So it also has advantages of code-first approach. But for me its API became less intuitive comparing with Migrator (Table.AddColumn, DataService.GetSchema().AddColumn() or simple AddColumn() – what should I used when just want to add column? Only one of them worked in my sample migration – simple AddColumn(). And to investigate what went wrong I forced to use SQL profiler – because sonic.exe didn’t show necessary output diagnostics information). I saw the same confusing questions in forums also. Also it should be mentioned that current verion 3.0 doesn’t have sonic.exe utility in distributive package (the latest version which contains migration utility is 2.2 built on .Net 2.0). Of course mentioned disadvantages may be caused by my poor experience in Subsonic and there are other ways to do migrations in 3.0 version.

One important thing which was mentioned on meeting is that all mentioned tools are suitable for scenarios with “flat” database schema lifetime (i.e. when versions are increased in one direction). But in real life there are often scenarios when there are multiple branches of db schema (e.g. for different set of features or for different customers) which are developed in parallel. And these tools do not greatly help to address these situations – they help to migrate schema only within single branch. And additional efforts should be performed to migrate schemas from different branches.

As result of comparing mentioned tools I made a decision that advantages of Tarantino.Net is more valuable for me and I will use this tool for automation of db migrations in my work. Concluding I would also thanks all guys from SPb ALT.Net for organizing this meeting.