Saturday, September 29, 2012

Speaking on Finland Sharepoint user group

On this week I spoke on the Finland Sharepoint user group. Theme of the speech was “Using of advanced C# features in Sharepoint development”, where I combined posts from the blog which are related with C# somehow and showed how with some creativity you may solve problems, which can’t be solved by only standard ways. Presentation is uploaded on SlideShare here. As always it was interesting to meet colleagues and listen to other speakers. Especially presentation of Gunnar Peipman (ASP.Net/IIS MVP), who spoke about optimization of Sharepoint sites for internet. Thanks to Jussi Roine, organizer of these meetings in Helsinki, and everyone who came to this event.

Saturday, September 22, 2012

Write C# code in powershell scripts

Powershell is powerfull sciprting language which allows you to automate many administrative tasks. However in addition to own language, it allows you to embed C# code inside script and execute it with other script instructions. This feature is not used very often, and although it will be good that you will teach powershell syntax, knowledge of this technique may help you in different scenarios. In this post I will show how to use C# inside powershell.

Lets move the following simple C# function to powershell and call it from ps1 file:

   1: public static class ScriptExample
   2: {
   3:     public static void EnumerateWebs(string url)
   4:     {
   5:         using (var site = new SPSite(url))
   6:         {
   7:             foreach (SPWeb web in site.AllWebs)
   8:             {
   9:                 Console.WriteLine(web.Url);
  10:                 web.Dispose();
  11:             }
  12:         }
  13:     }
  14: }

It opens SPSite object using passed url and enumerates all sub sites in this site collection. It is quite easy to write the same code in powershell, but our purpose is to show how to embed it into ps1 file and execute it from here. Here is the code:

   1: $assemblies = (
   2:     "Microsoft.SharePoint, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
   3:     )
   5: $source = @"
   6: using System;
   7: using Microsoft.SharePoint;
   9: namespace Test
  10: {
  11:     public static class ScriptExample
  12:     {
  13:         public static void EnumerateWebs(string url)
  14:         {
  15:             using (SPSite site = new SPSite(url))
  16:             {
  17:                 foreach (SPWeb web in site.AllWebs)
  18:                 {
  19:                     Console.WriteLine(web.Url);
  20:                     web.Dispose();
  21:                 }
  22:             }
  23:         }
  24:     }
  25: }
  26: "@
  28: $url = ""
  29: Add-Type -ReferencedAssemblies $assemblies -TypeDefinition $source -Language CSharp 
  30: [Test.ScriptExample]::EnumerateWebs($url)

At first we created list of imported assemblies (lines 1-3). In this case we need only Microsoft.SharePoint. If you will run this script from Sharepoint management shell, it is not needed, because this assembly is loaded automatically there, i.e. it is needed only if you run the script from regular powershell context. Then we write C# code and assign it to the local variable $source as string (lines 5-26). This is exact copy of our C# program shown above (it is important to add “using” directives there. Without them code won’t be compiled). After that we define url in local variable, load our C# code using Add-Type cmdlet with all additional assemblies which should be loaded into the context and call our function with parameter defined in powershell (lines 28-30). After running this scipt will print all sub sites of specified site collection.

As you can see it is quite easy to call C# managed code inside powershell. It may be helpful if you need to write some complicated code in ps1 file and don’t know how to move it to powershell syntax completely. Also it may be useful for developers who know C# and recently started working with powershell, although of course you anyway should teach powershell syntax in order to create more efficient code.

Saturday, September 8, 2012

Max number of nesting forests in cross-forest membership in AD groups

Some time ago we needed to determine what is the maximum number of nesting forests in cross-forest AD groups membership. There are 3 types of AD group scopes:

  • Universal
  • Global
  • Domain local

Group scope determines what members group may have and to what domains in the forest/tree you may assign permissions to this group. In the following technet article there is a summary table which describes possible members for each group scope, target domains where you can set permissions and to what group scope each of them can be converted:

Group scope Group can include as members…

Group can be assigned permissions in…

Group scope can be converted to…

Accounts from any domain within the forest in which this Universal Group resides

Global groups from any domain within the forest in which this Universal Group resides

Universal groups from any domain within the forest in which this Universal Group resides

Any domain or forest

Domain local

Global (as long as no other universal groups exist as members)


Accounts from the same domain as the parent global group

Global groups from the same domain as the parent global group

Member permissions can be assigned in any domain Universal (as long as it is not a member of any other global groups)
Domain local

Accounts from any domain

Global groups from any domain

Universal groups from any domain

Domain local groups but only from the same domain as the parent domain local group

Member permissions can be assigned only within the same domain as the parent domain local group Universal (as long as no other domain local groups exist as members)

Based on this table we may make interesting conclusion: cross-forest membership is possible for one level of nesting only (one hop). Only Domain local groups may have members from another forest: user accounts, Global and Universal groups, but not Domain local groups. Domain local groups may have as members another Domain local groups only if these child groups are from the same domain as parent group. At the same time Global and Universal groups may have members only from their own domain/forest. So maximum number of different nesting forests in cross-forest membership is 1. You may add groups from many forests into Domain local group, but nesting level is not greater than 1. Probably MS did it consciously in order to limit the complexity of maintenance and avoid circular dependency problems (e.g. if it would be possible to add into Domain local group from forest A as members Domain local groups from forest B then there will be possible that groups have each other as a member). This information may be useful if you work with multi-forest environments and need to plan security membership.

Content editor web part’s menu is not opened in Sharepoint site

Sometimes on the publishing pages with content editor web parts you may encounter with the problem, that menus of some web parts are not opened, while another web parts still work properly. I.e. if you need to add/edit/remove some content from web part the only way is to copy content from browser, then close or delete web part from maintenance page (which is opened if you add “?contents=1” to the url of the current page, e.g., add new web part and insert copied content there. However it may not fix the problem, i.e. when next time you will come to the page in order to edit it, you may face with the same problems. How to fix it?

In most cases such problems are related with content which is inserted to the web part. E.g. it may look Ok for end users, but not have correct html. In order to check it, run IE developer tools javascript console, start debugging and see whether or not there are any javascript errors when you open web part’s menu. If content is incorrect you may get the exception:

Error: 'style.display' is null or not an object.

which occurs in MSOWebPartPage_OpenMenu() function from ie55up.js file on the following line:

   1: if(minOption)
   2: {
   3: = (WebPart.getAttribute('allowMinimize')=='false' ||
   4:'none') ? 'none' : '';
   5: }

In order to fix the problem copy content without formatting (e.g. into notepad), then close problematic web part, add content to the new web part and re-apply formatting. Save changes and check web part’s menu after that. It should be opened successfully so you will be able to continue working with your web parts in regular way.

Sunday, September 2, 2012

Debug issues on production Sharepoint farm

In Sharepoint development it is not unusual when you have multiple working environments: development, QA, production. Development environment in most cases is single-farm environment, while QA is similar to production and has several WFEs, app and db server. Also in multi-vendors project it may be so that you as software provider don’t have access to QA and production: it is under control of another company responsible for IT infrastructure.

Such projects require more accurate development and quality assurance. However it still may happens that solution works properly on dev env, but after deploying it to QA problem occurs. What to do in such situation? How to troubleshoot issues if you even don’t have remote desktop access?

You need mechanism which is powerful and flexible enough to figure out where problem comes from and doesn’t require a lot of efforts from infrastructure maintenance company. One of the most efficient way to troubleshoot in such situation which I found during working over many multi-vendor projects is to create custom application layouts page, ask administrator from infrastructure company to copy it to 14/layouts subfolder on one of WFE and open it here in context of production site.

Page itself may have any logic implemented via server code. Code may be embedded into aspx page as server-side script:

   1: <%@ Page Language="C#" %>
   2: <%@ Assembly Name="Microsoft.SharePoint, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
   3: <%@ Import Namespace="Microsoft.SharePoint" %>
   4: <%@ Import Namespace="System.Web" %>
   6: <%
   2:     this.lbl.Text = SPContext.Current.Web.CurrentUser.Name;
   8: CurrentUser:&nbsp;<asp:Label ID="lbl" runat="server" />

By default you may use C# 2.0 in server-side scripts. In one of my previous posts I wrote how to enable C# 3.0 in application layouts pages: see Use C# 3.0 features in application layout pages in Sharepoint.

If you need to test a lot of code it may require time to embed the codebehind code to aspx page. There is another way to execute server code: with it you will have aspx page and separate .cs file with the logic. The method is based on CodeFile attribute for Page directive. In this case codebehind class will be compiled in runtime by ASP.Net. You need to specify path to .cs file in this attribute and then in Inherits attribute specify page class from this .cs file. Here is example:

   1: <%@ Page Language="C#" CodeFile="~/_layouts/test/Test.aspx.cs" Inherits="MyNamespace.Test" %>
   2: <%@ Assembly Name="Microsoft.SharePoint, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
   3: <%@ Import Namespace="Microsoft.SharePoint" %>
   4: <%@ Import Namespace="System.Web" %>
   6: CurrentUser:<asp:Label ID="lbl" runat="server" />

In Test.aspx.cs you need to specify base class of the page:

   1: using System;
   2: using System.IO;
   3: using System.Web;
   4: using Microsoft.SharePoint;
   6: namespace MyNamespace
   7: {
   8:     public partial class Test : LayoutsPageBase
   9:     {
  10:         public override void OnLoad(EventArgs e)
  11:         {
  12:             this.lbl.Text = SPContext.Current.Web.CurrentUser.Name;
  13:         }
  14:     }
  15: }

Note that there is no need to specify protected controls variables in page class. They will be added automatically in runtime by ASP.Net compiler in partial class (that’s why you need to make your custom page class partial as it is shown in example above). This is quite powerful technique which will allow to test existing application layout pages almost without changing them. Also it requires minimum actions from farm administrator from infrastructure maintenance company which in real life is very important advantage.