Corners in IE and Firefox for SharePoint MasterPages

You may find that the OOTB V4 master page has the Meta tag: <meta http-equiv=”X-UA-Compatible” content=”IE=8″/>. This renders the page in IE8 mode even when using IE9.

Web Designers may realise that this means CSS3 rounded corners are not available (http://www.css3.info/preview/rounded-border/). Unfortunately removing this tag breaks SharePoint’s ability to drag and drop web parts between web part zones and a few other quirky things.

Rounded corners in CSS3 use anti-aliasing to give a clean smooth effect. It also renders very fast. Using jQuery.corner.js sometimes slows the page down and causes a slight flicker, the rendering is not as sharp as the CSS3.

Rounded web part zone

To optimise the loading so that IE uses jQuery rounded corners and all other modern browsers use CSS3, I used the following code:

<!–[if IE]>

<script type=”text/javascript”>

jQuery(document).ready(function() {

jQuery(‘.right-wp-zone-core-1’).corner(“12px”);

});

</script>

<![endif]–>

<style type=”text/css”>

.right-wp-zone-core-1{

-moz-border-radius: 12px;

border-radius: 12px;

}

</style>

A bit disappointed with the rendering in IE9 but ensures that all browsers get the best experience possible.

The problem is not that CSS3 doesn’t work with IE 9 (it does very well!) but SharePoint 2010 does not work with IE9 (e.g. dragging web parts, people picker, etc…) so Microsoft force with default Master Page into IE8 mode using the meta tag.

Hopefully there is an update to IE9 soon (maybe automatic??) to fix SharePoint compatibility and then we can all happily use CSS rounded corners!!

SharePoint Designer 2010 opening MasterPages and ASPX files as text

I recently installed SharePoint Designer 2007 on my new Windows 7 64-bit laptop after installing SharePoint Designer 2010 on the same machine. When I tried to open a MasterPage or newitem.aspx list form, it would only open in SharePoint designer as text with all the options in the ribbon greyed out.

The reason for this is the ASPX, ASP, Master, etc were set to open as text in SharePoint Designer 2010. To change this, open SharePoint Designer 2010 and do the following:

  1. File –> Options
  2. Select the General tab
  3. Click “Application Options” next to “SharePoint Designer Application:”
  4. Select the “Configure Editors” tab
  5. Select ASPX ASCX ASHX….
  6. Make Sure that “SharePoint Designer (Open as HTML)”
  7. Click OK

Using XSLT, AJAX and jQuery to improve OOTB SharePoint Lists

Using XSL can completely change the way that any standard list view is displayed. There is a great video from Laura Rogers demonstrating what can be done with the XSLT List View in SharePoint Designer here.

XSLT
You can also build your own XSL sheets and use them in the style library by linking them in the web part properties.

Here is a standard web part view of a custom list:

Here is a standard web part view with custom XSL applied:

COM, jQuery and AJAX
Calling the SharePoint Client Object model with JavaScript, we can add items to the list without refreshing the page.
Inside the web part’s AJAX settings we can set the auto refresh so the list displays the new item (again without refreshing the page).
In this example, jQuery sets a fade-in animated gif to show the item being added to the list once send has been pressed. The fade-out effect is applied to the gif once success has been returned to the JavaScript function.
Building solutions like this can create complete customisable solutions using only custom lists in a modern integrated (no refresh) style.

Apply Custom Master Page to all Site Collections

I’ve just upgraded my MOSS 2007 site to SharePoint 2010. Oh No! The Master Page for 2007 doesn’t work in 2010 and I have 1500 Site Collections each with three websites!!

I need to activate my Master Page feature on each site collection and apply it to every site. Back in the old days, I would enumerate sites and create a huge script in excel to activate features and apply the Master Page. This is quite time consuming especially if you are upgrading different web applications every day. I thought it was time I tried to make this job easier and faster using PowerShell.

First start by looping though all the site collections in the web application then loop through the all the websites in the site collection (nested loop to get to every web in the web application). Then retrieve the URL of the web application and the URL of the site collection.
If the site is using a managed path for the site collections, it needs to be the relative path to the Master Page from the root of the web application.

e.g.

Web Application: http://www.tonyishere.co.uk
Site Collection: http://www.tonyishere.co.uk/sites/accounts
So the Master Page URL would be:  /sites/accounts/_catalogs/masterpages/custom.master

So how do we find the MasterPage URL path (as seen above) for each site collection? Using the length of the URL strings, the web application URL can be broken into a substring and appended to create the right path (see below).

e.g.
$len2 = $webname.length - 1
$len = $webappname.length
$len3 = $len2 - $len
$masterpath = $webUrl.substring($len,$len3)
$masterpath = $masterpath += "/_catalogs/masterpage/custom.master"

The new Master Page path then needs to be applied to the website.

$web.CustomMasterUrl = $masterpath

I found looping through 4500 websites in 1500 site collections took about 9 seconds, compared with the four hours that stsadm would of taken….big difference!!

Here is my full script to activate the feature on the site collection and loop into each website and apply the Master Page.


$webapp = Get-SPWebApplication https://www.tonyishere.co.uk
foreach ($s in $webapp.sites)
{
Enable-SPFeature MyCustomMasterPageFeature -Url $s.url
foreach ($web in $s.AllWebs)
{
$webUrl = $web.url
$web = Get-SPWeb $webUrl
$webappname = $webapp.url
$len2 = $webname.length - 1
$len = $webappname.length
$len3 = $len2 - $len
$masterpath = $webUrl.substring($len,$len3)
$masterpath = $masterpath += "/_catalogs/masterpage/custom.master"
$web.CustomMasterUrl = $masterpath
$web.MasterUrl = $masterpath
$web.Update()
}
}

European SharePoint Best Practice Conference 2011

bestPracticesLogo

I attended the SharePoint Best Practices Conference 2011 in April and came away with lots of great handy tips. Here is a quick summary of some of what I came across.

IIS caches user AD group membership –

You may have wondered why adding someone to an AD security group does not give someone instant access to a SharePoint site? Although SharePoint’s people picker reads directly from Active Directory, the actual group membership of clients is cached in IIS. So if you have accessed a site before and recently been added to a group in AD, it does not guarantee instant access to the new SharePoint sites. Even if the AD imports are run from Central Admin, it does not guarantee access to the site.

Using PowerShell to search error logs-

I went to a couple of talks by world famous SharePoint expert Gary La Pointe which was based on the use of PowerShell with SharePoint. He did a talk on using PowerShell for administrators and another talk on the use of PowerShell for developers. One of the things that always disappoints me is seeing another correlation ID appearing in a SharePoint error. Now using PowerShell we can write a function to quickly search the log files and return the recent matches to our correlation ID.

Get-SPLogEvent -StartTime (Get-Date).AddMinutes(-10) | ? {$_.Correlation -eq “8db5e7ed-075c-46cc-8d7c-e2cb78f15f7e”}

http://blog.falchionconsulting.com/index.php/2011/04/european-sharepoint-best-practices-conference-wrap-up/

It was a very eye opening talk by Gary and really made me realise the potential of PowerShell as an administration tool and also a very powerful development tool. Note that you can call these PowerShell commands/functions from c# code making some very interesting application pages.

Updating Display Name in profiles-

When a user logs into a SP2010 site, their display name in displayed in the top right corner of the page (when using v4 master). Sometimes a user may need their display name updating (e.g. marriage, legal, etc…). This is usually changed in AD and expected to replicate through to the SharePoint site.

This doesn’t always happen automatically and the reason for this is that if someone has not contributed to a site collection, then the site collection will not check that the profile information is up to date. One solution to this would be to delete the user from the site collection and get them to access it again. The most obvious solution though would be getting the user to contribute to the site collection in some way (adding announcement, uploading a file, etc…). Once the user has contributed to the site and the sync-profiles job has run, the display name will be updated!

Mysite Provisioning in 2010-

If your profile is not found when the profile sync job is run (your profile has probably been deleted from AD), then your profile is flagged for deletion by SharePoint. It is not deleted automatically in SharePoint 2010. The user will no longer be able to login. Public access is stopped and the manager of the user is now the site collection administrator. The manager is atomically sent an email asking them to backup any data on the site before it is deleted after x days.  The Mysite clean-up job will delete any users flagged for deletion. This will not remove the user from the info table (this is so you can still see who created/modified documents still on the system). There are 14 timer jobs relating to social and profile imports!

If a user is disabled in AD and later re-enabled, you will have to reinsert the profile property (URL) manually into the profile. If a user has been recreated (deleted and made again), the only option is to create a new Mysite and migrate any content across manually. Even with the same username, all the permissioning will be broken as AD uses the GUID rather than the actual username to permission items.

There was far more and much more detail but unfortunately I have run out of time. My train journey is coming to an end so I have to wrap up.

Will try to get more up soon….

Tony Phillips

Visual Upgrade Grayed out at top level site

All the sites managed the visual upgrade except the top level site. You can run this Powershell script to upgrade all sites in a site collection:

$site = Get-SPSite(“https://webappurl“)

foreach ($web in $site.AllWebs) { $web.UIVersion = 4; $web.Update() }

 

After running this, the top level site was in preview mode with a link in a yellow bar giving the option to upgrade the site. Click on the yellow bar and it will take you to the following page:

https://webappurl/_layouts/prjsetng.aspx

In the Visual Upgrade options, select “

note: 

I think you could probably do this at a top level site by going to https://webappurl/_layouts/prjsetng.aspx and selecting upgrade interface and then the buttons would be ungrayed. Best practice to do a visual upgrade in powershell and loop through all the sites in a site collection.

SharePoint 2010 Centered MasterPages

I spent a lot of time creating centered MasterPages for SharePoint 2010 using Adventure Works and Minimal MasterPages as starting point.

There was a number of issues when using these pages (as starting points). I struggled to get the QuickLaunch to appear on the adventure works MasterPage without distorting the content and also struggled with compatibility with IE7 and other random combinations of browsers. Starting from scratch with the minimal MasterPages creates a huge amount of work rewriting all of the CSS (although this would be the preferred method if I had time!)

In the end I went back and started to make my MasterPage based on the V4 master. Here is how I managed to get it to work by using CSS and very minor changes to the MasterPage itself. I based it around blogs I had read by Randy Drisgill and a few of my own trial and error experiences http://blog.drisgill.com/2010/12/my-real-world-branding-with-sharepoint.html.

I set the class s4-nosetwidth to the workspace div (this stops SharePoint setting the width automatically). We can then wrap our content in a table (single cell) or a div. This will be the container for the page. The reason we dont set a width to the workspace is because this would put the scroll bar in the center of the page.

<div id=”s4-workspace” class=”s4-nosetwidth”>
<table class=”s4-workspace-maintable”>
<tr><td>
<div id=”s4-bodyContainer”>

In the CSS file, you just need to set the width and centering of this table….simple! I have also included a fix for IE7 which crops content. This is a bug found in the standard V4 master. You may still find some issues using IE7 with this MasterPage but much less than using the Adventure works master or using the V4 master with the alternative method of centering.

CSS

/*——————-Browser Fixes ———————*/
/* fixes IE7 cropping*/
.ms-rte-layoutszone-outer
{
float:none!important;
}
/*—————End of Browser Fixes ——————*/

/* —————width and centering——————-*/
.s4-workspace-maintable{
background:white!important;
/*Fixed width*/
width:95%!important;
/* Center the page */
margin: 0 auto!important;
}
/* ———end of width and centering——————*/

MOSS 2007 and windows server 2008 IIS web dav

Windows 2008 SP2 or R2 doesn’t have web dav installed on IIS by default.

SharePoint actually uses it’s own web dav, so IIS web dav does not need to be installed. If you install IIS web dav on Server 2008 SP2 or R2, it will conflict with the SharePoint web dav and cause the explorer view, upload multiple documents, upload pictures to stop working when connecting using a windows 7 client.

Hope you find this tip useful….

Using Javascript to replace rendered HTML

If you want to replace HTML in a rendered page client side, you may wish to use something like the code below. It is particularly useful in a management system where it is difficult to edit the source code. Replacing the html client side can be a good work around.

Put this function in your header:

<script type=”text/javascript”>
function findAndReplace(searchText, replacement, searchNode) {
if (!searchText || typeof replacement === ‘undefined’) {
// Throw error here if you want…
return;
}
var regex = typeof searchText === ‘string’ ?
new RegExp(searchText, ‘g’) : searchText,
childNodes = (searchNode || document.body).childNodes,
cnLength = childNodes.length,
excludes = ‘html,head,style,title,link,meta,script,object,iframe’;
while (cnLength–) {
var currentNode = childNodes[cnLength];
if (currentNode.nodeType === 1 &&
(excludes + ‘,’).indexOf(currentNode.nodeName.toLowerCase() + ‘,’) === -1) {
arguments.callee(searchText, replacement, currentNode);
}
if (currentNode.nodeType !== 3 || !regex.test(currentNode.data) ) {
continue;
}
var parent = currentNode.parentNode,
frag = (function(){
var html = currentNode.data.replace(regex, replacement),
wrap = document.createElement(‘div’),
frag = document.createDocumentFragment();
wrap.innerHTML = html;
while (wrap.firstChild) {
frag.appendChild(wrap.firstChild);
}
return frag;
})();
parent.insertBefore(frag, currentNode);
parent.removeChild(currentNode);
}
}
</script>

Run the function after the page has loaded, include the text to replace as a variable (Current Text, New Text):

findAndReplace(‘Current Text’, ‘New Text’);