Welcome! I'm a Microsoft Certified Trainer (MCT) and a SharePoint MVP specializing in SharePoint, .Net, SQL Server and related technologies. I'm a Senior Instructor for MAX Technical Training (Cincinnati area).

You can stay up to date with my blog stuff using Follow TechTrainNotes on Twitter or

Search: or browse by a tag list of contents or browse the newest articles

Looking for a SharePoint user group? Cincinnati SharePoint User Group

12/29/2009

SharePoint: Add a “You are leaving this site” message to a links list.

 

This article is a variation of “http://techtrainingnotes.blogspot.com/2009/07/sharepoint-adding-popups-to-link-lists.html” and makes just one small change:

Replace:

links[i].target="_blank“

With:

links[i].onclick=function () {alert('you are now leaving this site')}

 

The links list web part is a quick and easy to add a list of links to vendors, other SharePoint sites or the most popular documents in a library. The problem is that the links list web part does warn users that they are about to leave your site (and you may have a legal reason to tell them so).

A typical links list:

            image

To change a links list to display an exit message:

  • Add a Content Editor Web Part (CEWP) just below the links list web part
  • Modify the CEWP and set a title then click Source Editor
  • Copy and paste the HTML and JavaScript below
  • Edit the JavaScript to change the word “Links” to the name of your links list web part (title of the web part, not the title of the actual list, although they may be the same)
    if (x(i).summary=="Links")     to
    if (x(i).summary=="Your Links List Title")
    (If you are not sure, display your page, select Source from the View menu and search for “.summary”)
  • Also… in the Advanced section of the CEWP’s properties change Chrome to “None” to hide the CEWP
  • Exit the edit mode and see if it works

 

<script>
// CEWP trick from techtrainingnotes.blogspot.com!
// Find the link list  (change "Links" to your web part's name)
var x = document.getElementsByTagName("TABLE") // find all of the Tables 
  var LinkList
  var i=0;
  for (i=0;i<x.length;i++) 
  {
    if (x[i].summary=="Links")
    {
      //
      LinkList = x[i];
      break;
     } 
  }

// add a target to the <A> tags
var links = LinkList.getElementsByTagName("A") // find all of the 
  for (i=0;i<links.length;i++) 
  {
    links[i].onclick=function () {alert('you are now leaving this site')}
  }
</script>

 

 

.

12/23/2009

SharePoint: Wiki Search and Replace

 

Note: The following works in both SharePoint 2007 and 2010. Just be aware that the 2010 “Site Pages” library is also a wiki library!

 

Recently I was asked about how to do a search and replace on text in all of the articles in a wiki. As there is no built-in way to do this I wrote a simple web page to do this.

Note that the example below is trivial and:

  • Is presented as a learning exercise
  • Does not include a master page
  • Must be deployed to the Layouts folder of the web servers
  • Is case sensitive
  • Is over all not too flexible… but it is presented to get you started on your own nice looking page…
Background:
  • A wiki is a library (and a library is just a fancy list)
  • The content displayed in a wiki is stored in a column of the library named “WikiField” (creative huh!)
  • Accessing a list and updating data via the SharePoint API is pretty easy
What you could do to make this better:
  • Just about anything…  ;-)
  • Add support for “Ignore Case”
  • Add a checkbox list of what was found and let the user decide to replace or not
  • Add the application master page and make this look like SharePoint
  • Write it as a little Windows application that calls the SharePoint web services so nothing has to be deployed to the web servers

Here’s what the page looks like:
image

To create this you will need to create a web page (ASPX) and a code behind file and copy them to the LAYOUTS folder on the web front end servers. You will then access the page by going to the site with the wiki(s) and navigating to the new page:
   http://yourserver/sites/yoursite/_layouts/WikiSearchReplace.aspx

 

What’s the <SharePoint:FormDigest runat="server" /> about? See here.

The ASPX page:  (WikiSearchReplace.aspx)

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="WikiSearchReplace.aspx.cs"
  Inherits="WikiSearchReplace" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls"
  Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>Untitled Page</title>
</head>
<body>
  <form id="form1" runat="server">
    <SharePoint:FormDigest runat="server" />
    <div>
      Wiki Libraries:
      <asp:DropDownList ID="ddlWikis" runat="server">
      </asp:DropDownList><br />
      <br />
      <table>
        <tr>
          <td>
            Find what:</td>
          <td>
            <asp:TextBox ID="txtFind" runat="server" /></td>
        </tr>
        <tr>
          <td>
            Replace with:</td>
          <td>
            <asp:TextBox ID="txtReplace" runat="server" /></td>
        </tr>
      </table>
      <asp:Button ID="btnReplace" runat="server" Text="Replace" OnClick="btnReplace_Click" />
      <br />
      <asp:Label ID="lblResults" runat="server" />
    </div>
  </form>
</body>
</html>

and here’s the code behind: (WikiSearchReplace.aspx.cs)
using System;
using System.Web;
using Microsoft.SharePoint;

public partial class WikiSearchReplace : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Get a list of all WIKIs in the site and add to the dropdown
        // WIKI = SPListTemplateType.WebPageLibrary
        if (!IsPostBack)
        {
            SPWeb web = SPContext.Current.Web;
            foreach (SPList lst in web.Lists)
            {
                if (lst.BaseTemplate == SPListTemplateType.WebPageLibrary)
                {
                    ddlWikis.Items.Add(lst.Title);
                }
            }
        }
    }

    protected void btnReplace_Click(object sender, EventArgs e)
    {
        // Get the current site
        using (SPWeb web = SPContext.Current.Web)
        {
            SPList list = web.Lists[ddlWikis.SelectedItem.ToString()];

            int articleCount = 0;
            int occurrenceCount = 0;
            string wikiText = "";

            foreach (SPListItem wikiItem in list.Items)
            {
                wikiText = wikiItem["WikiField"].ToString();
                if (wikiText.Contains(txtFind.Text))
                {
                    articleCount++;

                    // a trick to get the word cound
                    int count = (wikiText.Length - wikiText.Replace(txtFind.Text, "").Length) / txtFind.Text.Length;
                    occurrenceCount += count;

                    wikiItem["WikiField"] = wikiText.Replace(txtFind.Text, txtReplace.Text);
                    wikiItem.Update();
                }
            }
            lblResults.Text = articleCount.ToString() + " articles updated, " + occurrenceCount.ToString() + " occurrences updated";
        }
    }
}

SharePoint: The security validation for this page is invalid

 

Note: The following applies to both SharePoint 2007 and 2010.


When creating an ASPX page in the LAYOUTS folder that updates SharePoint content via the API (mylistitem.upate) you may get the following message when posting back to the page:

 

2007:
image

2010:
image

Many articles on the web suggest using AllowUnsafeUpdates:

SPWeb web = SPContext.Current.Web;
web.AllowUnsafeUpdates = true;


While this works, it does open the page up to cross-site scripting vulnerabilities. (See here: MSDN)

A better practice is to add a FormDigest control to your page. (See details here: MSDN)  If you are not using a master page or a complete “SharePoint page” then you will also need to add a Register line to reference Microsoft.SharePoint.WebControls.

The reference:

<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

The control:

  <SharePoint:FormDigest runat=server/>

A sample page:

 

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>


<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<SharePoint:FormDigest runat=server/>
<div>
<asp:TextBox ID="txtSomeText" runat="server" />
<asp:Button ID="btnReplace" runat="server" Text="Replace" OnClick="btnDoSomeWork_Click" />
</div>
</form>
</body>
</html>






.

12/21/2009

SharePoint: Worst SharePoint Error Message?

(MOSS 2007)

“WSS_Search_servername on servername contains user-defined schema. Databases must be empty before they can be used. Delete all of the tables, stored procedures and other objects or use a different database.”

 

Just got this on one of my virtual machines:

image

 

I just love this part: “Delete all of the tables, stored procedures and other objects or use a different database.” That’s kind of like responding to “I can’t connect to the internet” with “format drive C: and start over”.

Either the developer was having a bad day or has a real attitude!

 

Is this important?   Not really!   WSS Search is only used in MOSS to index the SharePoint help files, and this only needs to be done once (at least until the next service pack potentially updates the search content).  And, have you ever found this help content to be very helpful?

So for now… help works, but is just not searchable. You can still navigation the table of contents. (But I do have a fix at the end of this article…)

 

No results: But should look like this:
image image

                                                           

 

How I got the error:

This message was displayed in Central Administration when I tried to start the search services. I’m not sure why they were stopped. I may have stopped them to improve performance on the VPC or they may have just stopped.

image

 

 

A similar error was logged in the Windows Event log:

image

 

 

 

The fix?

Go to Central Administration to the services on server list and click start for the WSS Search. Enter a new database name!  I just appended a “B” to the end of the existing name.  (You can now go to your SQL admin tools and delete the old database if you like.)  I also had to do an IISRESET, but could have probably waited and it would have reindexed.

And… after you get it working and the content has been index, stop it!  Or at least set the schedule so it only indexes once a night. The Help content NEVER CHANGES!

image

12/20/2009

SharePoint: DataView Group By Expand / Collapse bug fixed!

 

One of the more popular articles I’ve written in this blog  is “Group By on more than 2 columns in a view”. As it has been over a year now, I decided to update the article and clean it up a bit. One of the problems many people had with it was not my solution, but how SharePoint handled the clicks on the Expand and Collapse buttons in the web part.  When you clicked Expand at the top level it also expanded all the detail levels.

As background, SharePoint Views support grouping, but to only two levels. My article shows how to convert a View into a DataView web part and how to add as many levels of grouping you want. Many people thought that was cool, but complained that clicking the expand button expanded way too much.

After converting to a DataView and grouping (and adding a bit of color), the list looks like this:

image 

Clicking on the second item expanded all of the detail below it, which made drill down painful.

image

After some research I found that the expand/collapse buttons call a JavaScript routine named ExpGroupBy that is found in CORE.JS. This routine was written to use the same code to expand and to collapse. When you collapse a higher level you also want to collapse all of the lower levels. Well, that then meant that expanding also expanded all lower levels!

 

The Fix…

I started with part of the ExpGroupBy code and added my own code to hand one level at a time expansion.

The DataView still starts out like this:

image 

But now it expands only one level at a time:

image

And you can then drill down and only expose what you need!

image

 

The Code…

First… where do you want to put this code?  I only needed it on the page with the grouped view, so I added a Content Editor Web Part to that page and used the Source Editor button to add the JavaScript. Do not modify CORE.JS! Besides being a generally bad idea, it will probably be overwritten on the next service pack update. You could add this to a linked JavaScript file in the master page that is loaded after the CORE.JS library, or even add the code to the master page itself.

Like all of my tricks (hacks?), you are on your own on this… and the safest place for this kind of code is in a Content Editor Web Part.

 

Note: Only tested with SharePoint 2007 and Internet Explorer (so far).

(If you have problems with Copy and Paste, copy and then paste in to Notepad, then copy and paste from there.)

 

 

 
<script>
// More JavaScript tricks from TechTrainingNotes.blogspot.com!
// Fix the way a DataView expands/collapse 
 
//wait until all of the SharePoint stuff is loaded...
_spBodyOnLoadFunctionNames.push("CustomExpGroupByFix"); 
 
 
function CustomExpGroupByFix() {
  //Replace the function in CORE.JS with our custom function
  window.ExpGroupBy=function(x){CustomExpGroupBy(x)}
}
 
 
// The customized version of ExpGroupBy
// based on ExpGroupBy found in CORE.JS
function CustomExpGroupBy(formObj)
{
  if (browseris.nav)
    return;
 
  if ((browseris.w3c) && (!browseris.ie)) {
    document.all=document.getElementsByTagName("*");
  }
 
  docElts=document.all;
  numElts=docElts.length;
  images=formObj.getElementsByTagName("IMG");
  img=images[0];
  srcPath=img.src;
  index=srcPath.lastIndexOf("/");
  imgName=srcPath.slice(index+1);
 
 
  if (imgName=='plus.gif')
  {
    fOpen=true;
    displayStr="block";
    img.src='/_layouts/images/minus.gif';
  } 
  else
  {
    fOpen=false;
    displayStr="none";
    img.src='/_layouts/images/plus.gif';
  }
  oldName=img.name;
  img.name=img.alt;
  img.alt=oldName;
  spanNode=img;
 
  //scan up to find TR with GROUPx ID
  var groupid=""
 
  while(spanNode !=null)
  {
    spanNode=spanNode.parentElement;
    if (spanNode !=null &&
        spanNode.id !=null &&
        spanNode.id.length > 5 &&
        spanNode.id.substr(0, 5)=="group")
      break;
  }
 
  groupid=spanNode.id;
 
  var startrow = spanNode.rowIndex;
 
  //scan up farther to find TABLE
 
  parentNode=spanNode;
  while(parentNode !=null)
  {
    parentNode=parentNode.parentElement;
    if (parentNode !=null &&
        parentNode.tagName.toUpperCase()=="TABLE")
      break;
  }
 
  trs = parentNode.rows;
 
  var datarowsnext = false;
  if (trs[startrow+1].id=="")
  {
    datarowsnext = true;
  }
 
  groupidnext = "group" + (parseInt(groupid.substr(5,1)) + 1)
 
  for (i=startrow+1;i<trs.length;i++)
  {
    if ( ((trs[i].id == groupidnext) || datarowsnext || !fOpen) 
           & (trs[i].id != groupid) )
    {
      trs[i].style.display = displayStr;
      if (trs[i].id == groupidnext)
      {
        img = trs[i].getElementsByTagName("IMG");
        img[0].src = '/_layouts/images/plus.gif';
      }
 
    }
 
    if ( trs[i].id == groupid) 
    {
      //then we have hit the next group at this level
      return;
    }
 
  }
 
}
</script>
 

12/07/2009

SharePoint 2010: Change the passphrase

 

When you install SharePoint using an option other than Stand-Alone you will be asked for a “Passphrase” that can be used later on for things like adding a new server to the farm. This is especially needed if you let SharePoint 2010 manage your service account passwords where SharePoint will automatically change the passwords to strong, but unknown passwords.

I don’t think there is anyway to retrieve the passphrase if you forget it. If you need to change the passphrase after the install you can use PowerShell. (Don’t know PowerShell yet? You will…) 

  • From your Start menu select “SharePoint 2010 Management Shell” or Start, “Microsoft SharePoint 2010 Products”, “SharePoint 2010 Management Shell”
  • Then enter:

      C:\PS> $passphrase = ConvertTo-SecureString -asPlainText -Force
      C:\PS> Set-SPPassPhrase -PassPhrase $passphrase -Confirm

The first line will prompt you for a password and store the secure version in the $passphrase variable.
The second line will prompt you to confirm the passphrase and then ask you if you are REALLY sure you want to do this.

 

image

 

I have not seen anything in TechNet on this command yet. For help on this command from PowerShell type:

    help passphrase

      or

    help passphrase  -examples

image

12/06/2009

Silverlight: Useful links

Below are some of the resources I refer to in my Silverlight classes.

These include:

  • Official Sites
  • Tutorial Sites
  • Best places to ask questions (forums)
  • What’s new in Silverlight 3
  • What’s new in Silverlight 4
  • Downloads
  • Other useful links

 

Official Sites:

 

They both claim to be “the official site”!

Sites for Expression Blend:

MSDN Library for Silverlight

 

Tutorial Sites:

 

 

Best places to ask questions:


MSDN forums:

Silverlight.net Forums:
(As of 12/5/09: 178,572 threads and 222,975 posts, contributed by 67,146 members from around the world!)

  • http://forums.silverlight.net/default.aspx
    • Installation and Setup
    • Getting Started
    • Hosting and Streaming
    • New Features in Silverlight 3
    • Silverlight 4 Beta
    • Designing with Silverlight
    • Video and Media
    • Expression Studio
    • Programming with JavaScript
    • Programming with .NET – General
    • Silverlight Controls and Silverlight Toolkit
    • Visual Studio & Silverlight Development Tools
    • Report a Silverlight Bug
    • Accessing Web Services with Silverlight
    • Game Development
    • WCF RIA Services

 

What’s new in Silverlight 3:

 

What’s new in Silverlight 4:

 

Downloads:

Other useful links…

 

What percent of users actually have Silverlight installed?

Datasets and Data Tables with Silverlight:

As a .Net developer we know DataSets and DataTables, and we often deliver these via web services, but Silverlight does not directly support Datasets or Datatables. There are a few workarounds using LINQ, custom libraries and even a custom grid control…

Pushing data to Silverlight from a server

Dealing with Cross-Domain issues

About loading images (and URLs)

Silverlight to JavaScript, JavaScript to Silverlight:

 Silverlight and SharePoint

Note to spammers...

Spammers, don't waste your time... all posts are moderated. If your comment includes unrelated links, is advertising, or just pure spam, it will never been seen.


... ---