Thursday, 19 December 2013

C#: How do I sort a generic list?

There are two simple methods available: OrderBy/OrderByDescending and Sort. The one you choose depends on your intended outcome.

If you want 'in place' sorting (ie: within the existing object), then use
myItems.Sort((x,y) => string.Compare(x.MyProperty, y.MyProperty));

If you want to assign the outcome to a new variable, the use
var mySortedResults = myItems.OrderBy(x=> x.MyProperty).ToList()

Tuesday, 17 December 2013

SQL Server: Add a 'not null' column to an existing table

I tried to add a 'not null' column to an existing table, but SQL Server (rightfully) complained. I had two options - give the column a default value (no thanks) or add a nullable column and change it.

I chose the latter.

1. Add a nullable column
Alter table [Foo] add [MyFoo] int null
2. Set all the values to a value
Update [Foo] set [MyFoo] = 1
3. Set the column to be 'not null'
Alter table [Foo] Alter Column [MyFoo] int not null


Tuesday, 19 November 2013

Javascript: Get the current root url

This may not be the easiest way, but is certainly worked for me.

var url = $(location).attr('href').replace(window.location.pathname, '').replace(window.location.search, '');

Monday, 4 November 2013

SharePoint: How do I add and remove unique permissions in Powershell?

Here are two useful scripts to help you on your way:

function SetPermission($url, $list, $group, $permission)
{

$spWeb = Get-SPWeb $url
$selectedList = $spWeb.Lists[$list]

# Assign the "Contribute" RoleDefition to the site's visitors group
$visitorsSPGroup = $spWeb.Groups[$group]
If (! $selectedList.HasUniqueRoleAssignments) {
$selectedList.BreakRoleInheritance($true)
}
$assignment = New-Object Microsoft.SharePoint.SPRoleAssignment($visitorsSPGroup)
$assignment.RoleDefinitionBindings.Add(($spWeb.RoleDefinitions | Where-Object { $_.Type -eq $permission }))
$selectedList.RoleAssignments.Add($assignment)

$selectedList.Update()

$spWeb.Dispose()
}

function RemovePermission($url, $list, $group)
{
$spWeb = Get-SPWeb $url
$selectedList = $spWeb.Lists[$list]

$visitorsSPGroup = $spWeb.Groups[$group]

If (!$selectedList.HasUniqueRoleAssignments) {
$selectedList.BreakRoleInheritance($true)
}

$web.AllowUnsafeUpdates = $true;
[Microsoft.SharePoint.SPRoleAssignmentCollection] $spRoleAssignments = $selectedList.RoleAssignments

for ([int] $a = $spRoleAssignments.Count - 1; $a -ge 0; $a--)
{
if ($spRoleAssignments[$a].Member.Name -eq $group)
{
$spRoleAssignments.Remove($a);
}
}
$web.Dispose()
}

Sunday, 3 November 2013

PowerShell: Loop through all the sites in a site collection (not recursive)

$site = Get-SPSite "http://mysitetosearch.com"
$topWeb = Get-SPWeb $site.Url
$site | Get-SPWeb -limit all | ForEach-Object { Write-Host "Found $_.Name"; }

Monday, 28 October 2013

SharePoint 2013: HTTP 500 error when sending an email from a SharePoint Designer workflow

I recently encountered this problem for a client and the cause had me stumped. I did all the basic checking
1. Check the UPS was up to date
2. Made sure that the Workflow Manager was correctly installed
3. Made sure that the firewall was not blocking the WF manager port (12290 or 12291).

Still nothing.

The problem (it seems) comes from Document Library 'Advance Settings'; the document library had 'require checkout' set. I did not think anything of it, but once I removed it the workflow executed as expected.

Its a strange behaviour but at least its something to look at when trying to resolve the issue.

Thursday, 24 October 2013

SharePoint 2013: Can I add a document to a generic list?

The short answer is yes. In the advanced settings for a list, there is an 'enable attachments' setting.


Wednesday, 23 October 2013

SharePoint 2013: the given key was not present in the dictionary

I have been pulling my hair out over the last few days to resolve this issue.

I tried some of the suggestions:
1. make sure that the User Profile Service is up to date and make sure that a full synchronization has run.
2. make sure that the initiator accounts have access to Worflow History
3. make sure that the Doc library does not require checkout (or that your code handles it)

However the real solution to my problem came from SharePoint permissions. I had overridden the base permissions the on the Workflow Tasks list and the lists hosting the workflows and it seems that this caused the problem.

Once I reverted to the parents' permissions, the error disappeared.

The next problem was allowing users to approve their tasks. By default, users did not have 'approve' permissions on the list, so I needed to add a little code to my 'Tasks' web part to solve the problem; I gave the current user 'contribute' permissions on the list item so it could be moderated.

SPSecurity.RunWithElevatedPrivileges(() =>
{
using (SPWeb inner = new SPSite(dr["EncodedAbsUrl"].ToString()).OpenWeb())
{
string relativeUrl = dr["FileRef"].ToString().Substring(dr["FileRef"].ToString().IndexOf("#") + 1);
relativeUrl = relativeUrl.Substring(0, relativeUrl.LastIndexOf("/"));

SPList list = inner.GetList(dr["EncodedAbsUrl"] + relativeUrl);
SPListItem item = list.GetItemById(int.Parse(dr["ID"].ToString()));
SPRoleDefinition RoleDefinition = web.RoleDefinitions.GetByType(SPRoleType.Contributor);

SPUser user = SPContext.Current.Web.CurrentUser;

SPRoleAssignment RoleAssignment = new SPRoleAssignment(user.LoginName, user.Email, user.Name, "notes");
RoleAssignment.RoleDefinitionBindings.Add(RoleDefinition);

inner.AllowUnsafeUpdates = true;
if (!item.HasUniqueRoleAssignments)
{
item.BreakRoleInheritance(true);
}
item.RoleAssignments.Add(RoleAssignment);
item.Update();
inner.AllowUnsafeUpdates = false;

}
});


Thursday, 17 October 2013

SharePoint 2013: How do I set a custom page to be a default page for a site?

The solution is very simple; navigate to the page in SharePoint Designer 2013, right click and select 'Set as Home Page'.


Tuesday, 15 October 2013

SharePoint 2013: How can I tell if my page is in edit mode?

The solution lies in the object model: Microsoft.SharePoint.SPContext.Current.FormContext.FormMode
It returns a SPControlMode enumeration.

An example usage of the code is as follows:

if (Microsoft.SharePoint.SPContext.Current.FormContext.FormMode == SPControlMode.Display)
{
  // Do display stuff
}
else if ( Microsoft.SharePoint.SPContext.Current.FormContext.FormMode = SPControlMode.Edit )
{
  // Do edit stuff
}

SharePoint 2013: How do I access the Web Part Maintenance page?

I was working on a visual web part that was causing a lot of problems with the page hosting it. I need to remove it, but the 'Edit Page' was not working. The solution: navigate to the Web Part Maintenance page and remove it from there.

How do you get there: add ?contents=1 to the end of your page url.

For example, if my page is http://hostheader/pages/default.aspx, then the Web Part Maintenance page for that page is  http://hostheader/pages/default.aspx?contents=1


Tuesday, 8 October 2013

SharePoint 2013: Where is my Workflow History list?

I was trying to create a workflow, but the default 'Workflow History' list was nowhere to be see.
It appears that it was hidden, so I used PowerShell to resolve the problem:

$web = Get-SPWeb "http://myweb"
$list = $web.Lists["Workflow History"]
$list.Hidden = $false
$list.Update()


Monday, 7 October 2013

Sharepoint 2013: IE Compatibility issues (it renders really badly)

I was trying to get my masterpage to render properly in IE (aka mission impossible) and, after a stack of trying, came up with the following changes that fixed my problems:

1. In the masterpage, add
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>

This will cause Sharepoint to use the latest version of IE installed on the host machine. I tried forcing the version (IE=7), but that did not seem to give the required results.

2. In the main CSS file, add

HTML {
    font-size: 100%;
    white-space:normal;
}

#contentRow{
white-space:normal;
}

.ms-core-pageTitle{
font-size:inherit;
}

My CSS was shocking; the font sizes were terrible and the alignment was like my kids bedrooms - very messy. Setting the white-space and font-size resolved my issues.

Conditional CSS

Anyone who has tried to get CSS working with IE will know about the 'nuances' of the browser.

Thankfully, CSS gives us some conditional formatting methods to deal with these scenarios; you can apply class explicitly based on the browser type or version.

<!--[if IE]>
<style type="text/css">
    @font-face {
    font-family: "Arial";
    src: url("../new/over.ttf");
    src: local("Over the Rainbow"), url("../new/over.ttf");
    }
</style>
<![endif]-->

See here for more information

Sharepoint 2013: Override display for App web part

My current project requires some custom CSS with the minimum amount of customisation. Instead of writing a stack of custom controls, we can 'intercept' the apps as they are being rendered and change their CSS at runtime.

The following is a sample of how this might look (I called my file blah.js):

(function () {
    var overrideContext = {};
    overrideContext.Templates = {};
    overrideContext.Templates.Header = "<div class='myheader'>";
    overrideContext.Templates.Footer = "</div>"
    overrideContext.Templates.Item = customItem;
    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideContext);
})();

function customItem(ctx) {

    return '<div class="myheadercss"><h1>'+ctx.CurrentItem.Title+'</h1></div>'+
           '<div class="mycontentcss"><img class="myimagecss" src="'+ctx.CurrentItem.Image0+'" alt="Image" />'+
           '<h2>'+ctx.CurrentItem.Header0+'</h2><p class="mytextcss">'+ctx.CurrentItem.Content1+' <a href="'+ctx.CurrentItem.Link0+'" class="mylinkcss">Read More</a></p></div>'
}

I placed my .js file in masterpage/scripts, so in the 'Miscellaneous' section of the webpart properties, I can put a link to my file:

~sitecollection/_catalogs/masterpage/scripts/blah.js


Tuesday, 24 September 2013

SharePoint 2013: Where is 'Sign in as Another User'?

The 'Sign in as Another User' has been removed by default, but it can be easily added.

There are a few options at your disposal:

1. Run your favourite browser 'as another user' by right clicking on it.
2. Navigate to the url <http://mysite>/_layouts/closeConnection.aspx?loginasanotheruser=true
3. Perform the following steps to add it to the user menu:

a      Locate and then open the following file in a text editor: C:\Program Files\Common Files\Microsoft Shared\Web Server 
Extensions\15\TEMPLATE\CONTROLTEMPLATES\Welcome.ascx

b.      Add the following element before the existing "ID_RequestAccess" element:
<SharePoint:MenuItemTemplate runat="server" ID="ID_LoginAsDifferentUser" Text="<%$Resources:wss,personalactions_loginasdifferentuser%>" Description="<%$Resources:wss,personalactions_loginasdifferentuserdescription%>" MenuGroupId="100" Sequence="100" UseShortId="true" />

c.      Save the file.





SharePoint 2013: Navigation item disappears behind a control

My intranet homepage has a navigation menu displaying the large number of sub sites in the site collection. The problem with the menu items is that they were disappearing behind the user control at the top of the page.

Fortunately, there is an easy fix - thank you Z-Index

ul.dynamic{
z-index:999;
}

Sunday, 22 September 2013

SharePoint 2013: Adding user permissions to specific lists

The intranet I am working on requires limited access to certain document libraries. This is easy enough - as I make my way through my configuration file in PowerShell, I gave the required users access to the document libraries they could see.

The only problem is that the users did not have access to the root site, so the sub site did not appear in the Navigation Bar. I tried adding 'View Only' permissions at the end, but that undid all my good work.

The solution was a change of approach; give the users access to the site and remove access from the items they should not see.

Its always helpful to sharpen the saw.

$list = $web.Lists["MyList"]
$web.AllowUnsafeUpdates = $true

if ($list.HasUniqueRoleAssignments -eq $false)
{
$list.BreakRoleInheritance($true)
}

$user = $web.EnsureUser("Domain\user")
$list.RoleAssignments.Remove($user)

$list.Update()

$web.AllowUnsafeUpdates = $false
$web.Update()

$web.Dispose()




Sharepoint 2013: Get all active tasks using SPSiteDataQuery for the current user (My Tasks)

My project was an intranet, a sub site monster that needed a query to show all the tasks for the current user. There were two options - recursive code (no thank you) or SPSiteDataQuery.

The key to the code is to limit the list query to the appropriate list type (see here for a list from Microsoft). In my case, it was "<Lists ListTemplate='107' />".

Then add some simple CAML (thank you Camldesigner) and you are there.

My final query was as follows:

            SPSiteDataQuery q = new SPSiteDataQuery();
            q.Webs = "<Webs Scope='SiteCollection' />";
            q.Lists = "<Lists ListTemplate='107' />";
            q.Query = "<Where><And><Or><Membership Type='CurrentUserGroups'><FieldRef Name='AssignedTo'/></Membership><Eq><FieldRef Name='AssignedTo'></FieldRef><Value Type='Integer'><UserID/></Value></Eq></Or>
<Neq>
<FieldRef Name=’Status’ />
<Value Type=’Text’>Completed</Value>
</Neq>
</And>
</Where>";
            // Always add a row limit (I may need to pass a CAF test)
            q.RowLimit = 1000;

Two things I noticed during my development:
1. Do NOT run this under elevated privileges. The script uses the context of the current user, so using elevated privileges will return the tasks assigned to the Application Pool account.
2. I had to write the CAML query in a single line. I tried using @" .... " but the runtime compiler kept complaining about invalid characters.




Thursday, 19 September 2013

Check code quality with SPDisposeCheck

Adding SPDisposeCheck to your VS solution is a good way to find potential performance and best practices issues early in the development cycle.

The code can be downloaded from http://code.msdn.microsoft.com/spdisposecheck

The installed executable (if you use the default settings like I did) is stored in
"C:\Program Files (x86)\Microsoft\SharePoint Dispose Check\SPDisposeCheck.exe"

The final step is to add the following script as a post build event:

cd $(ProjectDir)

“C:\Program Files (x86)\Microsoft\SharePoint Dispose Check\SPDisposeCheck” “$(TargetPath)” > “$(ProjectName)”.SPDisposeCheck.log

findstr /C:”Total Found:” “$(ProjectName)”.SPDisposeCheck.log

This will write the 'Total Found' with the number of SPDisposeCheck errors generated by the tool

Sharepoint 2013: Log in as another user

Switching users in SharePoint can be a pain - there is not 'Log In as Another User' in the suitebar.

The simple solution is to navigate to ~sitecollection/_layouts/closeConnection.aspx?loginasanotheruser=true and you will be prompted to log in again.

Active Directory Properties

I recently created a carousel control that read a few properties from AD.

I found this post here very useful - an extensive list of available properties.

PowerShell: Restart a SharePoint Service

Starting and stopping a service in PowerShell is pretty straight forward: Start-SPServiceInstance and Stop-SPServiceInstance

The tricky bit is passing in the correct parameter - the identity of the Service Instance.

The service can be found using Get-SPServiceInstance | Select TypeName, Id.

This will provide a list of the services and their corresponding Ids. You can then cut and paste the Guid into the appropriate command:

Start-SPServiceInstance -Identity <Guid>

Sharepoint 2013: List name does not correspond to URL name

It can be quite frustrating when the name of a list does not correspond to the URL. 

For example, the default Images library maps to /publishingimages/. 

I had this problem a little while ago and the solution was staring me in the face - the answer is held in the object model. If you pass in the name of the list ("Images"), you can get the SPList the old fashioned way: 

SPList list = SPContext.Current.Web.Lists["Images"].

However, if you manufacture a link, you need to use list.RootFolder.Name (or SPContext.Current.Web.Lists["Images"].RootFolder.Name)

Tuesday, 17 September 2013

SharePoint: Visual Web Part CSS Reference

Adding a CSS reference to a visual web part is quite simple:

<SharePoint:CssLink ID="Test" runat="server" DefaultUrl="Url/blah.css" />

The problem comes when referencing a relative url with spaces in it. To URL Encode or not, that is the question.

Initially, I have the reference as DefaultUrl="/Style%20Library/blah.css", but it was not being picked up by the web part. When I opened the source of the page, I found why - the url was being URL encoded by Sharepoint. This means that the URL in the source was

<link rel="stylesheet" type="text/css" href="/Style%2520Library/blah.css"/>

The URL was now double encoded.

I removed the %20 from the Url to resolve the problem.

Sharepoint 2013: Deploy a master page through a feature

It seems that the deployment code required for a masterpage in SharePoint 2013 is a little different from previous versions.

The same logic applies - create a module and deploy it with a feature.

Here is a sample module code, using my custom masterpage 'MyMaster.master'

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="MasterPageModule" List="116" Url="_catalogs/masterpage">
    <File Path="MasterPageModule\MyMaster.master" Url="MyMaster.master" Type="GhostableInLibrary" />
  </Module>
</Elements>

And here is the code in the feature receiver on the FeatureActivated event.

SPSite site = properties.Feature.Parent as SPSite;
SPWeb curWeb = site.RootWeb;

//create full master url
Uri masterURI = new Uri(curWeb.Url + "/_catalogs/masterpage/MyMaster.master");

curWeb.MasterUrl = masterURI.AbsolutePath;
curWeb.CustomMasterUrl = masterURI.AbsolutePath;
curWeb.Update();


Monday, 16 September 2013

Sharepoint 2013: Create a site template (where is it?)

Here is an excellent article outlining how to resolve the problem.

The problem is that site template creation is disabled by default. This property can be easily changed through Sharepoint Designer (in the Site Options button -> change the value of 'SaveSiteAsTemplateEnabled' to 'true').

Navigate to ~site/_layouts/15/savetmpl.aspx and create the template.

SharePoint 2013: Add Managed Metatadata navigation nodes in Powershell

The following script will add some managed metadata for navigation purposes. It will create a term store call 'My Group' and a term set  called  'My Term Set'. It will then add navigation links to Google and Hotmail.

# define some variables
$defaultMetadataServiceProxy = "Managed Metadata Service"
$groupName = "My Group"
$termSetName="My Term Set Name

#create the function to add the terms
function CreateNavigationTerm($termSet, $termName, $link, $termstore)
{
Write-Host "Add $termName"
$term = $termset.CreateTerm("$termName",1033)
$term.SetLocalCustomProperty("_Sys_Nav_SimpleLinkUrl", $link)
$termstore.CommitAll()
Write-Host "Successfully added $termName"
}

#get central admin
$adminwebapp = Get-SPWebapplication -includecentraladministration | where {$_.IsAdministrationWebApplication}

$caSite=Get-SPSite $adminwebapp.Url

#get the Taxonomy session
$session = new-object Microsoft.SharePoint.Taxonomy.TaxonomySession($caSite)

#get the Term Store
$termstore = $session.TermStores | where {$_.Name -eq $defaultMetadataServiceProxy}

#create the group (if required)
$rootgroup = $termstore.Groups["$groupName"]
if ($rootgroup -eq $null)
{
$rootgroup = = $termstore.CreateGroup("$groupName");
}

#delete the term set (if it exists)
if ($rootgroup.TermSets["$termSetName"] -ne $null)
{
$rootgroup.TermSets["$termSetName"].Delete();
}

# create it anew
$termset = $rootgroup.CreateTermSet("$termSetName",1033)
$termset.SetCustomProperty("_Sys_Nav_IsNavigationTermSet", "True")

# add the navigation terms
CreateNavigationTerm $termset "Google" "http://www.google.com" $termstore
CreateNavigationTerm $termset "Hotmail" "http://www.hotmail.com" $termstore

Write-Host "Done!!"



Sunday, 8 September 2013

SharePoint 2013: Hiding custom masterpage images in the upload page

My current masterpage contains some custom images and a banner.

<div id="wrapper">
    <div id="logo">
          <a href="/"><img src="/Style%20Library/Images/MyLogo.jpg" alt="My Logo" /></a>
</div>
<div id="banner" />

and a little css around it

#banner{
background-image:url(/Style%20Library/Images/MyBanner.jpg);
background-repeat:no-repeat;
}

This makes the site look great until I try and upload a file to a document library - the banner is now appearing in the upload page.

One way to get rid of it is to use a little JQuery to remove the images.

$(document).ready(function () {
                var pathname = window.location.pathname.toLowerCase();  
             
                    if(( pathname.indexOf('_layouts/15/upload.aspx') >0 )
                  || (pathname.indexOf('_layouts/15/checkin.aspx') >0 ))
               {
                    $("div[Id='logo']").css('display','none');
                    $("div[Id='banner']").css('display','none');
               }
});

Thursday, 29 August 2013

Visual Studio 2012: No exports were found that match the constraint contact name

To fix this problem, simply clear/detete/rename %AppData%\..\Local\Microsoft\VisualStudio\11.0\ComponentModelCache

Prevent field validators from firing when editing a web part

I have a simple visual web part with a RequiredFieldValidator. The only problem is that when the web part properties are set, a postback is fired and the validator is complaining. The net result is that I cannot make any configuration changes to the web part.

The following code will resolve this problem:

    protected override void OnPreRender(EventArgs e)
        {
            WebPartManager mgr = WebPartManager.GetCurrentWebPartManager(Page);
            if (mgr.DisplayMode.Equals(mgr.SupportedDisplayModes["Browse"]))
            {
                myValidator.Enabled = true;
            }
            else
            {
                myValidator.Enabled = false;
            }

            base.OnPreRender(e);
        }

Monday, 26 August 2013

SharePoint Designer Web Cache

The web cache for SharePoint Designer sometimes causes problems and needs to be cleared. I was having this problem when adding a custom workflow activity (see here for the blog I used).

I found this blog (here) with the following script to clear the cache. It certainly saved me a lot of pain.

cd "%APPDATA%\Microsoft\Web Server Extensions\Cache"
del *.web /S /Q "%APPDATA%\Microsoft\Web Server Extensions\Cache"
cd "%USERPROFILE%\AppData\Local\Microsoft\WebsiteCache\"
rmdir /S /Q "%USERPROFILE%\AppData\Local\Microsoft\WebsiteCache\."
mkdir "%USERPROFILE%\AppData\Local\Microsoft\WebsiteCache"
dir "%APPDATA%\Microsoft\Web Server Extensions\Cache"
dir "%USERPROFILE%\AppData\Local\Microsoft\WebsiteCache"
pause

Friday, 23 August 2013

Powershell does not resolve dynamic variables

I have been working diligently on a script to create Sharepoint groups in Powershell. It sounds easy enough until you have to create iterated allocations. For example, a group needs to be allocated permissions to multiple lists. This is where the fun starts:

My code was as follows:
For($i=0; $i -lt $scopes.Length; $i++) {
if (($scopes[$i] -ne $null) -and ($scopes[$i] -ne ""))
{
$list = $web.Lists["$scopes[$i]"]

if ($list.HasUniqueRoleAssignments -eq $false)
{
$list.BreakRoleInheritance($true)
$list.Update()
}

$list.RoleAssignments.Add($assignment)

$list.Update()
}

It compiles and runs, but I was getting a lot of error messages about unassigned variables in the RoleAssignment addition. Very frustrating.

The problem was traced to $list = $web.Lists["$scopes[$i]"]
Powershell will not resolve the 'inner" variable, which cause the lookup to return a null value.

The solution was simple. Put the value in a variable and use the variable.
$name = $scopes[$i]
$list = $web.Lists["$name"]

I wish I had thought of that 2 hours ago.


Thursday, 22 August 2013

SharePoint 2013: UserAgent not available, file operations may not be optimized

I encountered this problem after creating a new document library 'App'. After a little searching, I found the following code to flush the BLOB cache  to resolve the problem.

$webApp = Get-SPWebApplication "http://ihaveablobproblem.com"
[Microsoft.SharePoint.Publishing.PublishingCache]::FlushBlobCache($webApp)

Wednesday, 21 August 2013

Find the field type enumerator for a Sharepoint field type in Powershell

I have been trying to add a new field to an SPWeb. It sounds simple enough, but it ended up being a little tricky. The problem came from the 'FieldType' column.

After a little research, I found that the easiest way is to use the corresponding Integer from the FieldType enumerator (see here for more details).

I ended up with the following code that returns the corresponding enumerator for the '$fieldType'. For example, '$fieldType = Integer' returns 1.

$int = [Convert]::ToInt32(( [Microsoft.SharePoint.SPFieldType]::GetValues([Microsoft.SharePoint.SPFieldType]) | Where-Object  { $_ -eq $fieldType }))

I can now add fields the easy way.

$web.Fields.Add("FieldName", $int, $CanIBeNullableOrNot)

Find the location of Central Admin in Sharepoint using Powershell

My current project requires creating a Taxonomy through the Metadata Service. The starting point for all this work is finding out where Central Administration lives.

The following code will return the WebApplication details:

$centralAdminWebApp = Get-SPWebApplication -includecentraladministration | where {$_.IsAdministrationWebApplication}


Tuesday, 20 August 2013

You cannot customize permission levels in a web site with inherited permission levels

The resolution to the problem is in the error message - the SPWeb has inherited permission levels that need to be overridden.

The following code will solve the problem.

if (!web.IsRootWeb && !web.HasUniqueRoleDefinitions)
{  
  web.RoleDefinitions.BreakInheritance(true, false);
}

Tuesday, 23 July 2013

SharePoint 2010: Why can I see the 'Alert Me' button on the ribbon?

Setting up an Alert in SharePoint is very easy, but not if you cannot find the 'Alert' button.

The button will only be displayed if you have an outgoing SMTP configured.

Go to Central Admin -> System Settings -> Configure outgoing e-mail settings.

Once the SMTP server is configured, the Alert option will appear in the ribbon

Monday, 22 July 2013

Send an Email form PowerShell using Google SMTP

The following code snippet will send an email from PowerShell using Googles SMTP server.

$EmailFrom="blah@gmail.com"
$EmailTo = "blah2@uxceclipse.com"
$Subject = "Test"
$Body = "Test Body"
$SMTPServer = "smtp.gmail.com"
$SMTPMessage = New-Object System.Net.Mail.MailMessage($EmailFrom,$EmailTo,$Subject,$Body)
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("blah@gmail.com", "Password");
$SMTPClient.Send($SMTPMessage)

SharePoint 2013 Developer tools

While wading through the Internet looking for useful SharePoint information, I can across this post with some interesting tools for SharePoint 2013.

http://www.sharepointcolumn.com/edition-5-sharepoint-2013-developer-tools/

Remove a SharePoint column in PowerShell

I was trying to remove an existing column from a list through the UI without any success. The solution lay in removing it through the 'back end' with my good friend PowerShell.

Here is the code:

$web = Get-SPWeb -identity http://your/site/name
$list = $web.Lists["Your List Name"]
$column = $list.Fields["Your Column Name"]
$column.Hidden = $false
$column.ReadOnlyField = $false
$column.Update()
$list.Fields.Delete($column)

Thursday, 11 July 2013

SharePoint email address column validation

I was looking for code to validate a list column as an email address. I came across this post ( thanks Chris Kent ) which gave me the answer:

=AND(
ISERROR(FIND(" ", [Email],1)),
IF(ISERROR(FIND("@", [Email],2)),
 FALSE,
 AND(
ISERROR(FIND("@",[Email], FIND("@", [Email],2)+1)),
 IF(ISERROR(FIND(".", [Email], FIND("@", [Email],2)+2)),
 FALSE,
 FIND(".", [Email], FIND("@", [Email],2)+2) < LEN([Email])
 ))) )

Its a little piece of magic ....

Tuesday, 9 July 2013

Operation aborted (Exception from HRESULT: 0x80004004 (E_ABORT))

This error is caused by a SQL Server log getting too large.

The solution is to shrink to offending database.

Or, if you are like me and have a SQL Server 2008 database for development, run the following script:

exec master..sp_msforeachdb 'ALTER DATABASE [?] SET RECOVERY SIMPLE; DBCC SHRINKDATABASE ([?], 1); ALTER DATABASE [?] SET RECOVERY FULL;'

Thursday, 4 July 2013

Visual Studio 2008: The language-neutral solution package was not found

I removed a WSP from the solution store and tried to redeploy my code through Visual Studio 2008. I encountered the following error:

The language-neutral solution package was not found.

I am not sure of the cause - but I do know how to fix it: Close Visual Studio and reopen it.

Old school, but effective.

SharePoint 2007: This solution contains two assemblies with the same name, or the SharePoint server already has an assembly with the specified name

I have been working on a MOSS deliverable for a new client. I configured my Debug properties of the project to point to the target web application, but when I press F5, I get the following message:

This solution contains two assemblies with the same name, or the SharePoint server already has an assembly with the specified name

After a bit of investigation, I found the culprit - I had added a post build event to push the dll into the GAC.

"C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\gacutil.exe" -i "$(TargetPath)"

This meant that Visual Studio 2008 was finding an assembly in the GAC when it was not expecting to find one.

The resolution was:
1. Remove the dll from the GAC
2. Remove the post build event

I could then rerun the deployment and continue fixing my code ....


Tuesday, 18 June 2013

ProcessBatchData Helper Class

There are a few ways to add multiple rows to a SharePoint list. The iterative approach (i.e.: line by line) is simple, but this pattern will fail a CAF report. The better way to get the data up is loading a batch file using the ProcessBatchData method on your trusty SPWeb object. (The official blurb on the method is here.)

The problem with this is that is requires a well formatted XML string. The easiest way to accomplish this is to writer a helper class and it is quite a life saver.

Here is the class, followed by a sample of how to use the code.

   public class ProcessBatchDataHelper
    {
        private const string ProcessBatchStartXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
        private const string ProcessBatchRowsStartXml = "<ows:Batch OnError=\"Continue\">";
        private const string ProcessBatchEndXml = @"</ows:Batch>";

        private List<BatchProcessMethod> _methods = new List<BatchProcessMethod>();
        private int _methodId;

        public ProcessBatchDataHelper()
        {
        }

        private string GetMethodId()
        {
            return String.Format("M{0}", ++_methodId);
        }

        public BatchProcessMethod NewAddMethod(Guid listId)
        {
            var method = new BatchProcessAddMethod(GetMethodId(), listId.ToString("D"));
            _methods.Add(method);
            return method;
        }

        public BatchProcessMethod NewUpdateMethod(Guid listId, string itemId)
        {
            var method = new BatchProcessUpdateMethod(GetMethodId(), listId.ToString("D"), itemId);
            _methods.Add(method);
            return method;
        }

        public void NewDeleteMethod(Guid listId, string itemId)
        {
            var method = new BatchProcessDeleteMethod(GetMethodId(), listId.ToString("D"), itemId);
            _methods.Add(method);
        }

        public string ToCAML()
        {
            StringBuilder caml = new StringBuilder(10000);
            caml.AppendLine(ProcessBatchDataHelper.ProcessBatchStartXml);
            caml.AppendLine(ProcessBatchDataHelper.ProcessBatchRowsStartXml);

            _methods.ForEach(method => caml.AppendLine(method.ToCAML()));

            caml.AppendLine(ProcessBatchDataHelper.ProcessBatchEndXml);

            return caml.ToString();
        }
    }

    public enum BatchProcessCommand { Save, Delete };

    public abstract class BatchProcessMethod
    {
        private const string ProcessBatchSetVarXml = "\t\t<SetVar Name=\"urn:schemas-microsoft-com:office:office#{0}\">{1}</SetVar>";
        private const string ProcessBatchMethodEndXml = "\t</Method>";
        private const string ProcessBatchMethodStartXml = "\t<Method ID=\"{0}\"><SetList>{1}</SetList><SetVar Name=\"ID\">{2}</SetVar><SetVar Name=\"Cmd\">{3}</SetVar>";

        private string _methodId;
        private string _listId;
        private string _itemId;
        private BatchProcessCommand _cmd;

        private List<string> _setVars = new List<string>();

        protected BatchProcessMethod(string methodId, string listId, string itemId, BatchProcessCommand cmd)
        {
            _methodId = methodId;
            _listId = listId;
            _itemId = itemId;
            _cmd = cmd;
        }

        public void SetVar(string varName, string val)
        {
            _setVars.Add(String.Format(BatchProcessMethod.ProcessBatchSetVarXml, varName, val ?? String.Empty));
        }

        public string ToCAML()
        {
            StringBuilder caml = new StringBuilder(5000);
            caml.AppendLine(String.Format(BatchProcessMethod.ProcessBatchMethodStartXml, _methodId, _listId, _itemId, _cmd.ToString()));

            _setVars.ForEach(var => caml.AppendLine(var));

            caml.AppendLine(BatchProcessMethod.ProcessBatchMethodEndXml);

            return caml.ToString();
        }
    }

    public sealed class BatchProcessAddMethod : BatchProcessMethod
    {
        internal BatchProcessAddMethod(string methodId, string listId)
            : base(methodId, listId, "New", BatchProcessCommand.Save)
        {
        }
    }

    public sealed class BatchProcessUpdateMethod : BatchProcessMethod
    {
        internal BatchProcessUpdateMethod(string methodId, string listId, string itemId)
            : base(methodId, listId, itemId, BatchProcessCommand.Save)
        {
        }
    }

    public sealed class BatchProcessDeleteMethod : BatchProcessMethod
    {
        internal BatchProcessDeleteMethod(string methodId, string listId, string itemId)
            : base(methodId, listId, itemId, BatchProcessCommand.Delete)
        {
        }
    }

The following code does a batch update for the list "list" to the second list "list2" (yes, I know the names are AWESOME). The portion to be aware of is the item loop - I am only setting the Title column, but it can easily be extended. I have added a helpful comment to point you in the right direction.

ProcessBatchData requires the internal column name - I have created a helper method called 'InternalName', which will extract the name from the title.

NOTE: This method  does not take into account the size of the source list - if there are 1000000 rows, it will be done in a single batch. It would probably be best to send the records in batches of 100.

       private void ProcessBatch()
        {
            string sourceListName = "list";
            string targetListName = "list2";
           
            using (SPSite site = new SPSite(@"http://mysite/"))
            {
                using (SPWeb web = site.RootWeb)
                {
                    SPList sourceList = web.Lists[sourceListName];
                    SPList targetList = web.Lists[targetListName];

                    ProcessBatchDataHelper cmd = new ProcessBatchDataHelper();

                     SPListItemCollection items = sourceList.Items;
                    foreach (SPListItem item in items)
                    {
                        BatchProcessMethod method = cmd.NewAddMethod(targetList.ID);

                        string titleField = InternalName("Title", sourceList);
                        string titleValue = item[titleField].ToString();

                        method.SetVar(titleField, titleValue);
                        // Add additional columns here
                        // method.SetVar(secondFieldInternalName, secondFieldValue);
                    }

                    web.ProcessBatchData(cmd.ToCAML());
                }
            }
        }

        private string InternalName(string displayName, SPList sourceList)
        {
            SPFieldCollection fields = sourceList.Fields;
            if (fields.ContainsField(displayName))
            {
                if (string.Compare(displayName, "Title", true) == 0)
                    return "Title";
                else
                    return fields[displayName].InternalName;
            }

            return string.Empty;
        }

Sunday, 16 June 2013

Token Replacement in Visual Studio

I was working on a simple WCF service, including a generic handler (.ashx). My code looked good until I tried to access the url - it started complaining that it did not know what '$SharePoint.Project.AssemblyFullName$' was.

A little digging ( here and here ) revealed at VS will not replace its tokens in all file types - you may need to specify them in the project file. Generic handlers are one of those types.

So, first I unloaded the project and added the following to the property group:

<PropertyGroup>
 <TokenReplacementFileExtensions>ashx</TokenReplacementFileExtensions>
</PropertyGroup>

I then reloaded the project and redeployed. Too easy.


Create a new SharePoint Farm: Local accounts should only be used in stand alone mode

I was creating my new VM and installed a SharePoint Farm on the local machine. Easy enough. All was good until I tried to run the configuration wizard.

That is when it all when wrong. I got the fantastic error message:

Local accounts should only be used in stand alone mode

Some simple searching brought my to this post ( here ) that gave me the solution. Basically, you need to run the installation in PowerShell (using New-SPConfigutionDatabase), as it exposes properties that are not available through the wizard. Once the database has been created, SharePoint thinks it has a Farm and the configuration wizard can continue on its merry way.

Its pretty simple when you know how ....




Tuesday, 4 June 2013

SharePoint 2010 Code Samples

I have come across the following code samples for SP 2010 development on the Microsoft site. It is full of helpful code snippets and solutions that will probably save me a lot of searching.

The samples are here


Thursday, 23 May 2013

Disable all event receivers in SharePoint 2010

I found the following page( here ) about disabling global event receivers. A fantastic post that solves a very specific need.

The crux of the solution is as follows:

public class MyClass
{
  SPListItem i = SPContext.Current.Web.Lists["Blah"].Items[0]
  HandleEventFiring handleEventFiring = new HandleEventFiring();
  handleEventFiring.DisableHandleEventFiring();

  try
  {
     i.Update();
  }
  finally
 {
    handleEventFiring.EnableHandleEventFiring();
  }
}

public class HandleEventFiring : SPItemEventReceiver
{
       public void DisableHandleEventFiring()
      {
          this.EventFiringEnabled = false;
       }

       public void EnableHandleEventFiring()
      {
          this.EventFiringEnabled = true;
       }

Cannot login to SharePoint 2010 site created as a Host Header Site Collection

I was creating a development environment for a fellow developer in a new VM. I had gone through my checklist for a new VM:

1. Create the Web Application
2. Create the Host Header Site Collection
3. Add the bindings in IIS
4. Update the hosts file to point 127.0.0.1 to the host header
5. Updated DisableLoopbackCheck ( see here )
6. Give all the users excessive permissions

Still nothing. The site would not render, even though the Farm Solutions where OK and my deployment log has not errors.

I found the solution in the post ( here ) and on the Microsoft site ( here ).

I needed to add a Multi-String registry key named BackConnectionHostNames to the registry at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0 and add the host headers.



SharePoint 2010 Client side script to check if a user is in a SharePoint group

The following script looks in the SharePoint object model if the current users exists in the SharePoint group 'My Group Name'. If the user exists, then the button 'My Button' is hidden:

ExecuteOrDelayUntilScriptLoaded(disableControls, "sp.js");

function disableControls() {
    clientContext = new SP.ClientContext.get_current();
    currentUser = clientContext.get_web().get_currentUser();
    clientContext.load(currentUser);
    this.collGroup = clientContext.get_web().get_siteGroups();
    clientContext.load(collGroup);
    clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}

function onQuerySucceeded() {
    var groupEnumerator = collGroup.getEnumerator();
    while (groupEnumerator.moveNext()) {
        var oGroup = groupEnumerator.get_current();
        if (oGroup.get_title() == "My Group Name") {
            users = oGroup.get_users();
            clientContext.load(users);
            clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceededFoundGroup), Function.createDelegate(this, this.onQueryFailed));
        }
    }
}

function onQuerySucceededFoundGroup(sender, args) {
    if (users.get_count() > 0) {
        UserExistInGroup = false;
        for (var i = 0; i < users.get_count(); i++) {
            if (users.itemAt(i).get_loginName() == this.currentUser.get_loginName()) {
                UserExistInGroup = true;
                break;
            }
        }
        if (UserExistInGroup) {
            $(':submit').each(function () { if ($(this).val() == "My Button") { $(this).hide(); } });
        }
    }
}

function onQueryFailed(sender, args) { }

Sunday, 19 May 2013

Error deploying Nintex workflow: An item with the same key has already been added

My client recently upgraded their version of Nintex Workflow 2010 and suddenly some of my workflows started to fail on deployment.

The following error cropped up:

Error deploying workflow: Server was unable to process request. ---> An item with
the same key has already been added. ---> An item with the same key has already
been added.

WTF????

A little digging unveiled the problem - I had two items in a 'Create Item' shape with the SAME DISPLAYNAME. I needed to move the second item to a new 'Update Item' shape in order for it to work.

I order to get the workflow to deploy, I had to manually remove the 'duplicate' item from the serialized XML. Hooray!!

It was then a simple matter of creating a new shape to update the 'duplicate' value.

Using Nintex 2010 to get a users name from the User Profile Service in Sharepoint 2010

The User Profile Service is powerful, but absolutely useless if you cannot access the information it stores.
The following code and shapes will let you extract this precious information and store it in a Nintex workflow variable.

I was looking to extract the users' name based on their login. Sounds easy, but ended being a little tricky.

The next few steps assume that the User Profile Service is configured and running on your server. (Goes without saying, but I wanted to say it anyway.)

First, call the web service.


and put the data in a variable.

Now the clever bit - xml tags and the appropraite xml namespace around the data so that we can interrogate the data with XPath.

<xml xmlns="http://microsoft.com/webservices/SharePointPortalServer/UserProfileService">
{WorkflowVariable:userProfileServiceReturnXML}
</xml>


and finally, write a clever XPath query to extract the required value.

//defaultNS:xml/defaultNS:Values/defaultNS:ValueData/defaultNS:Value

Awesome!




Wednesday, 8 May 2013

Hide My Site and My Profile in Sharepoint 2010 with CSS

My current project is in a shared environment, so disabling services and the like in Central Administration is not an option. However, the following code was able to remove the pesky menu options with a minimum of fuss.

The first attempt was to use a blanket css update:
#mp1_0_0_Anchor,#mp1_0_1_Anchor { display: none; }

but we then found that our menu items in a custom girdview control vanished. Bummer. The solution was narrow the scope of the css in order to be very specific for the menu items.

The following code was produced:

.ms-MenuUIUL li[text='My Site'] { display: none;}
.ms-MenuUIUL li[text='My Profile'] { display: none;}

Check for existance of a file in Sharepoint

I was recently writing some simple code to extract a file from a document library. The code was simple enough, but it was the return value that got me into trouble.

The code is as follows:

try
{
  using (SPSite site = new SPSite(http://blah.com))
  {
    using (SPWeb web = site.RootWeb())
    {
        SPFile file = web.GetFile("my file url");
    }
  }
}

All good so far. However, using my .Net brain is did a 'if (file != null)' .... oops.
The file object is never null.

The correct way to check the result if (if (file.Exists) .....).

Simple.

Monday, 15 April 2013

Filtering list information by SharePoint group in Sharepoint 2010

My current project requires that data be filtered based on the credentials of the current user.

The solution - a little magic in the view definition.

In a view, you can specify the following in the 'where' clause of the CAML:

<Membership Type="CurrentUserGroups">              
 <FieldRef Name="MyColumnToFilterData" />
 </Membership>

The only requirement is that the column my be of type 'Person or Group'.

See here for more information.

Tuesday, 12 March 2013

Sharepoint 2010: Increase workflow frequency in Powershell

My current project relies heavily on Nintex Workflow 2010 to process its workflows. The solution is littered with NWF files and I am generally at the mercy of the Sharepoint Timer Service about when to start the process.

I run the following script to increase the (default) frequency that the workflows are polled for work. It should be noted that although this is great for a development environment, but will probably cause more harm than good in a larger shared environment.
There may be some serious resource contention if 1000s of workflows are running every minute.

Anyway, on to the the script:

Write-Host "Setting workflow postpone threshold to 30"
Set-SpFarmConfig -WorkflowPostponeThreshold 30
Write-Host "Setting the polling frequency to evert minute"
get-sptimerjob | ?{$_.name -like "job-workflow"} | set-sptimerjob -schedule "every 1 minutes between 0 and 59"
Write-Host "Configuration Complete"

Tuesday, 5 March 2013

Windows 7 tools for capturing issues

It can be rather frustrating when a user tries (ineffectively) to explain a problem or the steps to reproduce it. I have found 2 fantastic tools that can make life a lot easier:

1. Snipping Tool - a build in image capturer that can be used to easily send screen shots (because Ctrl-PrntScreen is too hard)
2. Problem Steps Recorder - a tool to record the steps to reproduce a a problem. The days of issues being lost in translation or steps being left out are history.

If life was only about resolving bugs, software development would be a lot easier ....

Monday, 4 March 2013

Error 0x80070057 when using ProcessBatchData

I was recently using the SPWeb.ProcessBatchData function (more information here) when the following error code reared its ugly head:

0x80070057

wtf?

The  XML looked fine, but there was a sinister problem at play. The UTF declaration was in lowercase INSTEAD OF UPPERCASE.

Thats three hours of my life I am never getting back. Oh well, live and learn (and share!).

Get the internal column name in Sharepoint 2010 from the display name

The internal column is easily obtainable from the Sharepoint object model.

A recent function required me to use the internal name instead of the (provided) displayname. The following function resolved the problem quickly and easily:

private string InternalName(string displayName)
{
 SPFieldCollection fields = SPContext.Current.Web.Lists["MyListName"].Fields;
 if (fields.ContainsField(displayName))
 {
   if (string.Compare(displayName, "Title", true) == 0)
     return "Title";
   else
     return fields[displayName].InternalName;
 }
 throw new Exception("Column not found");
}

This function is not great, but it has solved my problem. The one caveat is the extracting of the 'Title' column. The function was returning 'LinkTitle', which caused the bulk update the fail quietly.

The hack fixed the problem. This is not my most elegant piece of code, but it does the business.





Wednesday, 27 February 2013

Purge Old Nintex 2010 Workflows

Nintex workflow can be a very helpful tool, but there is currently no automated way to remove orphan workflows. The workflow engine will still try and hydrate - run - dehydrate all 'running' workflows, even if they are attached to orphan lists.

I use the following code to remove all my workflows. Please note that this will remove EVERYTHING INCLUDING CURRENT WORKFLOWS. Its very helpful to speed up workflow response time and remove the dreaded 'Nintex is very busy. Please be patient' message.

The logic of the code is quite simple. First, generate a list of all the items in the nintex database (based on webId, siteId and listId) that do not exist in the specified content database. Then loop through all the records calling the OOTB PurgeWorkflow stored procedure.

You will need to change the variable at the top of the code to match your environment.
I have added a 'DATEADD' portion to the code, so that workflows for the last X days are saved.

Enough banter, show me the code.

declare @nintexDB varchar(255) = 'NW2010DB;

declare @wssDB varchar(255) = 'WSS_Content';

declare @sql varchar(max);

declare @id int = 0;

declare @workflowName varchar(255);

declare @workflowInstanceId uniqueidentifier;

declare @itemId int;

declare @state int;

if object_id('tempdb..#orphans') is not null

drop table #orphans;



create table #orphans

(

ID int not null identity,

WorkflowName varchar(255),

WorkflowInstanceID uniqueidentifier,

ItemID int,

[State] int,

StartTime datetime

)

create unique clustered index UCI on #orphans (ID);

select @sql =

'

insert into #orphans(WorkflowName, WorkflowInstanceID, ItemID, State, StartTime)

select A.WorkflowName, A.WorkflowInstanceID, A.ItemID, A.State, A.StartTime

from '
+ @nintexDb + '.dbo.WorkflowInstance A

join

(

select SiteID, WebID, ListID, ItemID

from '
+ @nintexDB + '.dbo.WorkflowInstance

EXCEPT

Select SiteID, WebID, ListID, doclibrowid

From '
+ @wssDB + '.dbo.AllDocs

) B

on A.SiteID = B.SiteID and A.WebID =B.WebID and A.ListId = B.ListID and A.ItemID = B.ItemID

and StartTime <= DATEADD(dd, -0, GetDate())'

EXEC (@sql);

while (@id is not null)

begin

select @id = min(ID) from #orphans where ID > @id;



if (@id is not null)

begin

select @workflowName = WorkflowName,

@workflowInstanceId = WorkflowInstanceID,

@itemId = ItemID,

@state = [State]

from #orphans

where ID = @id



set @sql = 'EXEC ' + @nintexDB + '.dbo.PurgeWorkflowData @instanceId = ' + '''' + convert(varchar(255), @workflowInstanceId) + '''';

EXEC (@sql);

end

end