Demo: Generating Sandboxed Solutions Through Code for SharePoint 2010

Some weeks ago I startet a new private dev project.

To reach one of the goals of the project I thought about the possibility to create Sandboxed Solutions at runtime. I’ve extracted this aspect of the project to show you how to create Sandboxed Solutions on the fly.

 

In my demo scenario for this blog article I’d like to have a custom list that enables me to create entries in the “Edit Control Block” at runtime. For every list item that is created in the custom list a Sandboxed Solution will be created and uploaded to the solution gallery of the site collection.

 

Let’s have a look:

 

1. First we look into the solution gallery of “http://sharepoint.local”. It’s empty.

image

2. I’ve created my demo solution and deployed it. This solution contains a list template and list instance. The list instance is shown in the Quick Launch as “Dynamic Menu”.

image

3. Now I create a new entry in this list:

image

image

4. Now we look into the solution gallery:

image

There is a new Sandboxed Solution in it!!

5. In the Shared Documents libary I have a single file “My PDF”. Now I open the files context menu:

image

image

6. After creating a new item in the “Dynamic Menu” I get a new entry in the Edit Control Menu:

image

image

This opens a new Outlook Window with the item URL as body.

image

7. For every generated solution there is an entry in the Site Collection Features list:

image

image

 

How does it work?

1. First I’ve created a list template and list instance.

2. Then I’ve created a Event Receiver.

image

There are two methods:

  • Utilities.RemoveSolution
  • Utilities.AddSolution

3. Method “Utilities.AddSolution”

This static method creates the Sandboxed Solution.

Therefore internal non-public methods of SharePoint are invoked. This is the most interesting aspect of the demo project!

ConstructorInfo ctor = typeof(Microsoft.SharePoint.SPSolutionExporter).Assembly.GetType("Microsoft.SharePoint.Utilities.Cab.CabinetInfo").GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0];
object cabInf = ctor.Invoke(new object[] { solutionFileName });

MethodInfo[] mi = cabInf.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
MethodInfo mi2 = null;
foreach( var m in mi )
    if( m.Name == "CompressDirectory" && m.GetParameters().Length == 4 )
    {
       mi2 = m;
       break;
    };

mi2.Invoke(cabInf, new object[] { solutionDir.FullName, true, -1, null });

 

In the source code before this snippet I create the structure of a WSP file in a temporary folder in the file system. The shown code creates the WSP file that I upload into the solution gallery.

4. Utilities.RemoveSolution

This static method removes the solution from the solution gallery.

5. You can download the complete code from codeplex:

http://spautogensolution.codeplex.com/

Minify JavaScript and PowerShell scripts by using PowerShell

There are several tools out there to minify JavaScript files. – You don’t know “minification”? Look here: http://en.wikipedia.org/wiki/Minification_(programming)

Common tools are:

For those of you that like PowerShell as I like it I’ve created a PowerShell module that let’s you minify JavaScript files and PowerShell script files.

Here it is on Codeplex:

http://minifyps.codeplex.com/

You can use the module like this:

cls

#module in the same path as the script:
Import-Module (join-path (split-path $MyInvocation.mycommand.path) "minjs")

#module in a default module path like "C:WindowsSystem32WindowsPowerShellv1.0ModulesminifyPS"
#Import-Module "minjs"

$str = @"
    var test = 'some JavascriptCode';
"@

$min = (minify -inputData $str -inputDataType "js")

[System.Windows.Forms.Clipboard]::SetText($min )

The module works like this:

1. Split script source code into typed chunks:

  • Code
  • Single Quoted String
  • Double Quoted String
  • Multiline Comment
  • End-of-line Comment

2. Remove all unwanted chunks such as comments

3. Process all remaining chunks by using regular expressions

4. Combine all processed chucks to a result string

 

For every language (JavaScript and PowerShell) there is a config section in the script.

image

There are two files in the package at Codeplex:

image

  • “minJS.psm1” => This is the module
  • “minJSDemo.ps1” => This is a demo script that uses the module

The demo script contains a piece of JavaScript that will be minified. And the script will minify itself and the module to an “output” sub directory that is created by the script if it does not exist.

This is a screenshot after running the demo script:

image

If you execute “minJSDemo.min.ps1” in the output folder you execute the minified version of “minJSDemo.ps1” and the minified version of the module itself (“minJSmin.psm1”).

 

The module is ALPHA at the moment. I’ve tested it in some projects and it seems to work as expected.

I’ve tested it with a huge JavaScript file: “core.debug.js” of Microsoft SharePoint:

C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions14TEMPLATELAYOUTS<lcid>CORE.debug.js

I’ve renamed the original file to “core.debug.js.bak”, minified it and saved the result as “code.debug.js”. So it’s used in the browser. (The minification of ~360KB takes 90 seconds.The minification module is slow…)  

It’s not possible to use the minified version. It seems that the original “code.debug.js” has some bugs. For example in line 42: There is no trailing “;”

image

But because of removing the line break this results in:

image

This causes the following JavaScript error in Internet Exlorer:

image

I’d be happy to hear about your experiences with the module. Please post comment, write me an e-mail or join the Codeplex-project!

Walkaround for common “TaxonomyPicker.ascx” error in Windows Event Log: SharePoint Feature that renames the file

There is a common error in SharePoint 2010: On almost every (web frontend) server you can find this error in the Windows Event Log “Application”:

Event Type: Error
Event Source: SharePoint Foundation
Event Category: None
Event ID: 7043
Computer: SERVERNAME
Description: Load control template file /_controltemplates/TaxonomyPicker.ascx failed: Could not load type ‘Microsoft.SharePoint.Portal.WebControls.TaxonomyPicker’ from assembly ‘Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c’.

image

The problem is described in many blogs and sites, e.g.:

To solve the problem there’s no other way than renaming the “TaxonomyPicker.ascx” file.

On a single-server farm it’s not that problem but in a huge farm with about 100 servers … ?

I’ve created simple SharePoint 2010 solution that will do the job for you.

The solution deploys a SharePoint Timer Job Definition and the farm feature event receiver creates two timer jobs for every web application on the system. The first timer job is a “one-time” job, the other one is a “daily” job. Each job checks the SharePoint hive on the local disk of every server and renames the “TaxonomyPicker.ascx” file to “TaxonomyPicker.ascx.disabled”, if the file is present. If the file “TaxonomyPicker.ascx” not present or already renamed it does nothing.

the “one-time” job runs some seconds after the feature activation and will delete itself after running. The “daily” job executes every day between 1am and 2am.

You can download this code at Codeplex. Please feel free to modify it, but please send me your improvements or request “edit” permissions on codeplex. – I’m sure there are possible improvements. It’s a “quick dev project”.

http://spdisabletaxpicker.codeplex.com/

When you disable the farm feature the file “TaxonomyPicker.ascx.disabled” is renamed back to “TaxonomyPicker.ascx”!

Be sure you test the feature in a dev system before you use it in a live system!!!! I only provide the source code he. Please compile it by yourself und use the solution as you want.