Sharepoint, 64-bit Office and Datasheet view

by Martin 25. november 2011 10:55

Some times you want to edit a list in datasheet view:

But you get an error, if you've installed the 64-bit version of Office:

This is resolved by installing 2007 Office System Driver: Data Connectivity Components

Tags: , ,

General | MOSS | SP2010

Creating Notes://-links in SharePoint 2010's Rich Text Editor (RTE)

by Martin 17. oktober 2011 08:15

I've been working on a project where we have migrated a customer from Lotus Notes to Exchange and SharePoint. On the SharePoint side a requirement arose to be able to link to document in the old Notes database. (Notes-links starts with notes:// in the URL and requires Lotus Notes to be installed on the client to be intepreted correct) We found out, that the Rich Text Editor in SP2010 won't allow notes://-links : they are banned by javascript in core.js (it only allows http, ftp, \\, and similar normal protocols). So, when creating a notes://-link in the RTE you are left with an empty a-tag with no href. No matter what you do! Either the client side javascript removes the link or the serverside when posting (if you manually have created the link in HTML-view).

So, the solution for us was actually not that easy. Some forums on the WWW suggests that you edit your core.js to contain the notes:// protocol in the array of allowed protocols. BUT, this will actually leave you in an unsupported state if you upgrade your farm (SP or CU's). That does not fly. So the solution for us kinda three-fold;

  1. Create an easy way for the editors to cvreate links by "mimicking" the normal RTE "Insert Link" (external) in SharePoint
  2. Create a "redirect service" on the server that transferred the user to Notes
  3. Make sure for max usability to get the user back to page they came from

The way I made the mimicking of the standard RTE "Insert Link" was to

  1. Create a button which was placed the same place as the other "Insert Link" shortcuts.
  2. Create a dialog that displayed the same interface as the standard "Insert Link". This was done by completely copying the existing dialog .aspx and changing a few lines
  3. Make this dialog return a url which was not truncated or cut out by the server side execution.

To create the button, the following Elements.xml was produced:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
               <CustomAction
                             Id="Intranet.CustomActions.NotesLink"
                             Location="CommandUI.Ribbon">
                             <CommandUIExtension>
                                            <CommandUIDefinitions>
                                                          <CommandUIDefinition
                                                                         Location="Ribbon.EditingTools.CPInsert.Links.InsertLink.Link.Menu.Link.Controls._children">
                                                                         <Button 
                                                                                        Id="Intranet.CustomActions.InsertNotesLink"
                                                                                        Command="RTE.RichTextEditor.insertNotesLink"
                                             Image16by16="/_layouts/$Resources:core,Language;/images/formatmap16x16.png" Image16by16Top="-160" Image16by16Left="-224"
                                                                                        Image32by32="/_layouts/$Resources:core,Language;/images/formatmap32x32.png" Image32by32Top="-160" Image32by32Left="-448"
                                                                                       Description="Inserts notes://-link"
                                                                                       LabelText="Insert link poiting to Notes"
                                                                                       Sequence="30"
                                                                                       TemplateAlias="o1"
                                                                                       ToolTipTitle="Indsæt notes link tool tip"
                                                                                       ToolTipDescription=""/>
                                                                                                      />
                                                          </CommandUIDefinition>
                                            </CommandUIDefinitions>
                                            <CommandUIHandlers>
                                                          <CommandUIHandler
                                                                         Command="RTE.RichTextEditor.insertNotesLink"
                                                                         CommandAction="javascript:RTE.RichTextEditor.insertNotesLink();"
                                                                         />
                                            </CommandUIHandlers>
                             </CommandUIExtension>
               </CustomAction>
</Elements>

The changes in the aspx where minor (some lines removed for readability, just copy (and rename to customerrtedialog.aspx) the existing rtedialog.aspx and replace the lines validating the protocol with this):

function OkButtonClick()
                                            {
                                                          var arr = new Array();
                                                          var valid = 1;
                                                          if ((document.getElementById(">")) != null)
                                                          {
                                                                         arr[0] = (document.getElementById("")).value;
                                                          }
                                                          if ((document.getElementById("")) != null)
                                                          {
                                                                         arr[1] = "/_layouts/Customer.Intranet/Customer.LotusNotesRedirect.aspx?target=" + encodeURIComponent((document.getElementById("")).value);
                                                          }
                                                          if (document.pageName == "SharePointLink")
                                                          {
                                                                         if (arr[0] == null || typeof(arr[0]) == 'undefined' || arr[0].trim().length == 0 ||
                                                                                       arr[1] == null || typeof(arr[1]) == 'undefined' || arr[1].trim().length == 0)
                                                                         {
                                                                                       alert("You must specify a list and an item.");
                                                                                       valid = false;
                                                                         }
                                                          }
                                                          if (document.pageName == "InsertTable")
                                                          {
                                                                         if (arr[0] < 1 ||
                                                                                        arr[1] < 1 ||
                                                                                       isNaN(Number(arr[0])) ||
                                                                                       isNaN(Number(arr[1])))
                                                                         {
                                                                                       alert("");
                                                                                       valid = 0;
                                                                         }
                                                                         if (valid && arr[0] * arr[1] > 625)
                                                                         {
                                                                                       alert("");
                                                                                       valid = 0;
                                                                         }
                                                          }
                                                          if (document.pageName == "CreateLink")
                                                          {
                                                                         valid = true;
                                                          }
                                                          if (valid)
                                                          {

                                                                         commonModalDialogClose(1 , arr);

                                                                         window.returnValue = arr;
                                                                         window.close();

                                                          }
                                            }

In the masterpage i added an EditMode-panel at the very bottom to reference the following javascript, which extends the existing functionallity. The javascript is put in a seperate .js-file.

RTE.RichTextEditor.insertNotesLink = function () {
               ULSkay: ;
               RTE.SnapshotManager.takeSnapshot();
               RTE.ObjectCommands.insertNotesLink();
               RTE.SnapshotManager.takeSnapshot();
}

RTE.ObjectCommands.insertNotesLink = function () {
               ULSkay: ;
               var $v_0 = RTE.Cursor.get_range();
               if (!$v_0.get_isEditable()) {
                             return;
               }
               if (!$v_0.$EP()) {
                             return;
               }
               var $v_1 = new RTE.InsertNotesLinkDialog();
               $v_1.show();
}


RTE.InsertNotesLinkDialog = function () {
               ULSkay: ;
               this.$85 = Function.createDelegate(this, this.$Dq_0);
}
RTE.InsertNotesLinkDialog.prototype = {

               show: function () {
                             ULSkay: ;
                             var $v_0 = new RTE.InsertLinkDialogArguments();
                             $v_0.allowRelativeLinks = false;
                             $v_0.text = RTE.Cursor.get_range().get_text();
                             RTE.DialogUtility.$5Q('CustomerRteDialog.aspx', 'CreateLink', true, true, $v_0, null, this.$85, false, 400);
               },

               $Dq_0: function ($p0, $p1) {
                             if ($p0 === 1) {
                                            var $v_0 = $p1;
                                            var $v_1 = $v_0[0];
                                            var $v_2 = $v_0[1];
                                            if (RTE.RteUtility.$9H($v_2, false)) {
                                                          var $v_3 = RTE.Cursor.get_range();
                                                          var $v_4 = $v_3.parentElement();
                                                          if (!$v_4) {
                                                                         return;
                                                          }
                                                          var $v_5 = $v_4.ownerDocument.createElement('A');
                                                          $v_5.href = $v_2;
                                                          if ($v_3.isEmpty()) {
                                                                         SP.UI.UIUtility.setInnerText($v_5, ($v_1.trim() !== '') ? $v_1 : $v_2);
                                                                         $v_3.insertBefore($v_5);
                                                          }
                                                          else {
                                                                         if ($v_1.trim() !== '') {
                                                                                       $v_3.replaceHtml($v_1);
                                                                         }
                                                                         $v_3.wrapRange($v_5);
                                                          }
                                                          $v_3.moveToEndOfNode($v_5);
                                                          RTE.Cursor.update();
                                                          RTE.RteUtility.showRibbonTab('Ribbon.Link', 'LinkTab');
                                            }
                             }
               }
}
RTE.InsertNotesLinkDialog.registerClass('RTE.InsertNotesLinkDialog');

What this script does is to prefix the given notes://-link with a valid http-link and encode the querystring (the notes url). When saving the page, SharePoint API won't be the wiser than to allow the url.
When the user clicks the link he/she is taken a to page on the SharePoint server which creates a "javascript-redirect" by using the window.location and setting it to the Notes://-url. After waiting 3 seconds, the user is then taken back to the page they came from (this is while Notes is opening up to show the document the link was pointing to.
ASP.Net code:

<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Customer.LotusNotesRedirect.aspx.cs" Inherits="Customer.Intranet.LAYOUTS.Customer.Intranet.LotusNotesRedirect" DynamicMasterPageFile="~masterurl/default.master" %>

<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<script type="text/javascript" language="JavaScript">
<!--
               function goBack() {
                             history.go(-1);
               }
               window.location = '<asp:Literal ID="uiLitRedirectLocation" runat="server" />';
               setTimeout(goBack, 3000);
//-->
</script>
</asp:Content>

Codebehind:

using System;


namespace Customer.Intranet.LAYOUTS.Customer.Intranet
{
               public partial class LotusNotesRedirect : Microsoft.SharePoint.WebControls.LayoutsPageBase
               {
                             protected void Page_Load(object sender, EventArgs e)
                             {
                                            if (string.IsNullOrEmpty(this.Page.Request["target"]))
                                            {
                                                          Microsoft.SharePoint.Utilities.SPUtility.TransferToErrorPage("Kan ikke omdirigere, da link ikke er angivet i \"target\"-querystring");
                                            }

                                            this.uiLitRedirectLocation.Text = System.Web.HttpUtility.HtmlDecode(this.Page.Request["target"]);
                             }
               }
}

Tags: , , , ,

SP2010

Lenovo ThinkPad noisy fan

by Martin 8. september 2011 14:31

I've been equipped with a state of the art Lenovo ThinkPad W520. A powerhouse! Runs smoothly with 6 Hyper-V guests running :)

It had one minor flaw; the fan was running at full speed for no apparent reason.

I found out that a tool exists to aid you to control the fan speed: TPFanControl (ThinkPadFanControl).

It's available here (at your own risk) for the vast majority of ThinkPads: http://www.staff.uni-marburg.de/~schmitzr/donate.html

Tags: , ,

General

IE9 adblocking

by Martin 10. august 2011 08:37

For a while I've been using Simple Adblock to get rid of ads on webpages. Simple Adblock blocks ads in IE9 which has several advantages:

  • Less loading time for pages; the browser doesn't waste bandwidth to get the ads
  • The intrusive ad corps don't get their money
  • I get a much cleaner looking page with the important content in focus

BUT, Simple Adblock comes in 2 flavors, one free which blocks 200 ads a day (which means 1 visit to berlingske.dk and 1 visit to eb.dk...) or a paid version for 29$ (which of course blocks all ads 'indefinetly'). I don't want to pay money to get rid of ads!

Enter TPL (Tracking Protection Lists), a new feature in IE9. Simply put it's a built-in adblocker in IE9. From this blogpost I got a link to how to easily configure the TPL in IE9. Essentially, one goes to http://www.quero.at/adblock_ie_tpl.php and add Quero's TPL to your browser: Poof, ads-be-gone!

Tags: ,

General

MCPD and MCITP

by Martin 22. juli 2011 21:56

I've now passed all the SharePoint 2010 exams.

Sooooo, know I'm a MCITP and a MCPD:

YEAH Cool

Tags: , ,

MCTS: 70-573 Microsoft SharePoint 2010 Application Development passed

by Martin 22. juni 2011 09:49

A couple of days ago I passed the MCTS exam, 70-573 Microsoft SharePoint 2010 Application Development. Sooooo, I can now use this logo:

I just wanted to let any interested candidates know how it was. I've using the web search engines to find information on how to pass this exam, but there isn't much info out there. The only good info was from Becky Bertram: http://blog.beckybertram.com/Lists/Exam%2070573%20Study%20Guide/AllItems.aspx

The preperation was a LOT of hands on experience with SharePoint 2010 development (countless projects). Futhermore I used the book "Professional SharePoint 2010 Development" by mulitple authors. It was a great help! Especially regarding workflow development where I did not have any hands on experience...

The exam itself was just as all the other Prometric exams; some easy questions, some hard and some tricky. There is a lot of guidance on how to complete the exam, but my advice is to choose "Mark for review" on the questions where you are in doubt. Revisit them later and read the question carefully once again, you might have missed something in the wording of the question.

Tags: , , ,

SP2010

Site column messing up Document Information Panel (DIP)

by Martin 4. januar 2011 12:54

I've been doing a project which requires custom site columns (who hasn't!?). These site columns where then added to document libraries. But the site columns didn't show in the Document Information Panel in Word 2010... Frown And it messed up all subsequent columns added after my custom columns (the columns added afterwards wasn't displayed either) They did show in the document properties in the SharePoint UI though...
The trouble seemed to be in the CAML for my site columns: I've forgotten that the StaticName-property doesn't allow spacings... Embarassed Correcting the spacings by either removing them (making the staticname-prop camel-cased) or replacing with _x0020_ did the trick! Now my custom columns show in the DIP! Laughing

Strange that the column works except for in the DIP... If somebody has an explanation, please comment on this post

Tags: ,

.NET

Custom site provisioning and navigation

by Martin 25. november 2010 15:13

I've been doing a project for a client requiring a custom site definition template.

This template included various custom lists to be instantiated along with the site definition. This worked like a charm. The lists had <data> elements inside the <list>-elements (in short: had folders created when provisioned, great article here: http://geekswithblogs.net/kit/archive/2008/08/25/124679.aspx ). This also worked fine until I found out that is was also a requirement to have links in the navigation on the left-hans side to the lists.

This is NOT easily accomplished!

Because if you set the OnQuickLaunch-property of a ListTemplate-element at best it adds it under a "Libraries"-heading in the navigation. I wanted it to be a link in the "root" of the navigation. A had to do this with a featurereceiver. Easily accomplished in it self but not in practice.

I added a feature-element to my CAML in onet.xml activating my feature when the site was provisioned. Problem was just that the lists wasn't created at this point, so I couldn't get a reference to them (I didn't want to hard code the URL's). In short: feature leveraging featurereceiver was activated before lists were instantiated. OK I thought: instead I'll just move the list instatiation to a separate feature, activate the feature instanting the lists in the CAML of my onet.xml. This led to a plethora of exceptions in my ULS and no site(s) being provisioned (something like "Column 'LinkTitle' does not exist" and various others...).

There was another alternative to accomplish my goal: Create an ExecuteUrl-element and call an applicationpage which activated the feature (and hence the featurereceiver). This didn't seem to work either.. I had the below code

 

protected void Page_Load(object sender, EventArgs e)
		{
			try
			{
				Guid featGuid = new Guid("70dc1ed8-5664-46cc-8068-310c033879b8");
				if (SPContext.Current.Web.Webs.Count > 0)
				{
					//Activate feature
					for (int i = 0; i < SPContext.Current.Web.Webs.Count; ++i )
					{
						using (SPWeb web = SPContext.Current.Web.Webs[i])
						{
							if (web.Features[featGuid] != null)
							{
								//Feature already activated, continue
								continue;
							}
							//Add feature to trigger feature receiver
							web.Features.Add(featGuid);
						}
					}
				}

				SPUtility.Redirect(SPContext.Current.Web.Url, SPRedirectFlags.Default, System.Web.HttpContext.Current);
			}
			catch { }
		}

This didn't work either: It threw an exception: Updates not allowed on GET requests. D'oh, Remember to set the AllowUnsafeUpdates-property to true AND revert it after you're done:

protected void Page_Load(object sender, EventArgs e)
		{
			try
			{
				Guid featGuid = new Guid("70dc1ed8-5664-46cc-8068-310c033879b8");
				if (SPContext.Current.Web.Webs.Count > 0)
				{
					//Activate feature
					for (int i = 0; i < SPContext.Current.Web.Webs.Count; ++i )
					{
						using (SPWeb web = SPContext.Current.Web.Webs[i])
						{
							if (web.Features[featGuid] != null)
							{
								//Feature already activated, continue
								continue;
							}
							bool allowUnsafeUpdates = web.AllowUnsafeUpdates;
							//Allow unsafe updates
							web.AllowUnsafeUpdates = true;
							//Add feature to trigger feature receiver
							web.Features.Add(featGuid);
							//Update
							web.Update();
							//Reverse setting
							web.AllowUnsafeUpdates = allowUnsafeUpdates;
							//Updaet again to reflect changes
							web.Update();
						}
					}
				}

				SPUtility.Redirect(SPContext.Current.Web.Url, SPRedirectFlags.Default, System.Web.HttpContext.Current);
			}
			catch { }
		}

Mission accomplished!

Tags: , , , ,

.NET

SharePoint 2010 Service Applications and PowerShell

by Martin 16. august 2010 15:11

I've been doing some configuring of my SP2010 test-bench. This involves configuring service applications with PowerShell scripting.
I found a superb reference in http://www.todd-carter.com/post/2010/04/26/The-Wizard-Likes-His-GUIDs.aspx to script creating applications!

One error I stumbled upon while running the script was:
"The term 'New-SPExcelServiceApplication' is not recognized as the name of a cmdlet" (a similar arror occured with Access services... figures...)

Hmmm, I thought my Enterprise CAL was enough... IT IS! But: somehow my installation was mixed up with confusion. It said in SCA that the Enterprise CAL was used. After a lot of digging, I found out that I had to go into "Upgrade and Migration" --> "Enable Features on Existing Sites":

In the "Enable Features..." page you have to check the "Enable all site in..." option and press OK

Wait for the upgrade to finish, and violá: the term 'New-SPExcelServiceApplication' is now available!

Tags: ,

Exporting existing wsp packages from SharePoint

by Martin 5. juli 2010 13:01

I'm currently working on a project with migrating from one SharePoint installation to another newer installation. For this project, the client has a couple of installed wsp's in the farm. But they do not have the wsp's to reinstall them... So I somehow had to write up some code that could export a given wsp from the farm. I ended up writing an extension for STSADM (yeah, I know, I should use PowerShell, but I'm an oldie for a little longer :-)). I thought, I'd share the end result with those who wanted to give it a go. (Use the supplied wsp at your own risk)

The source code goes here:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.StsAdmin;
using System.Collections.Specialized;
using Microsoft.SharePoint.Administration;

namespace ExportWSP
{
	public class ExportWSP : ISPStsadmCommand
	{
		public string GetHelpMessage(string command)
		{
			return "-wsp ";
		}

		public int Run(string command, StringDictionary keyValues, out string output)
		{
			if (!keyValues.ContainsKey("wsp") && !keyValues.ContainsKey("all"))
			{
				throw new InvalidOperationException(string.Format("USAGE: -eit-exportwsp -wsp  [-all]. You supplied wsp: {0}, all: {1}", keyValues["wsp"], keyValues["all"]));
			}

			try
			{
				this.ExportWSPPackage(keyValues);
			}
			catch (Exception ex)
			{
				output = ex.Message;
				return 1;
			}

			output = "Success";
			return 0;
		}

		private void ExportWSPPackage(StringDictionary keys)
		{
			if (keys.ContainsKey("all"))
			{
				foreach (SPSolution sol in SPFarm.Local.Solutions)
				{
					this.ExportWSPPackage(sol.Name);
				}
			}
			else
			{
				this.ExportWSPPackage(keys["wsp"]);
			}
		}

		private void ExportWSPPackage(string wspName)
		{
			//Get reference to solution
			SPSolution sol = SPFarm.Local.Solutions[wspName];

			if (sol != null)
			{
				//Save file (assuming it does so to the execution path
				sol.SolutionFile.SaveAs(wspName);
			}
			else
			{
				throw new InvalidOperationException("Solution does not seem to exist");
			}
		}
	}
}

Below is a link to the wsp-file. The project has been built with STSDev a tool I absolutely love when developing SharePoint solutions!

To use the extension, simply use one of the following commands:

stsadm -o eit-exportwsp -all , or
stsadm -o eit-exportwsp -wsp <NAME OF WSP TO EXPORT>

The first line exports all the wsp's in the farm to the folder the command is executed from. The second export the given wsp to the folder the command is executed from.

Parameters explained:
-all: Exports ALL WSP's in the farm
-wsp <NAME OF WSP TO EXPORT>: exports a single wsp from the farm.

To install the solution, simply use:

stsadm -o addsolution -f exportwsp.wsp
stsadm -o deploysolution -n ExportWSP.wsp -imm -allowgac
stsadm -o execadmsvcjobs

 

ExportWSP.wsp (8,21 kb)

Tags: , , , ,

.NET | MOSS

Page List

About

Take a look at my LinkedIn-profile.

DISCALIMER: The content on this blog is in NO way affiliated with my employer. The opinions expressed on this blog are solely my own and in NO way endorsed by my employer.

Recent comments

Comment RSS

Month List