Tips for Devs #1: Enumerate Farm Sites and Pages
23. June 2009 08:14

I've decided to do a little series on SharePoint Tips for Developers, which will focus on common tasks that aren't immediately obvious, hopefully saving people some time in their day to day work.

Our first tip is about enumerating the objects in your farm.  Specifically, taking an SPFarm object, finding all the web applications in the farm, all the sites, and all the pages within them.  Some portion of that enumeration is likely to come up quite often in your work.

First, Getting all Web Applications in a farm is probably one of the lesser known tasks:

SPWebService webService = farm.Services.GetValue<SPWebService>("");
foreach (SPWebApplication webApp in webService.WebApplications)
{
//Do Something with Web Application here
}

In the above example, the variable farm is an SPFarm object, which you can get in a variety of ways, depending where your code is running.  In a timer job you might just do "this.Farm" -- in a web part you can get the farm from the site object in SPContext.Current.


Now we can go through all the sites and webs.  Probably the simplest way to do this is to loop through the Sites collection of your web app, and call a recursive helper function on each web, which will enumerate through all child webs regardless of depth.

        foreach (SPSite site in webApp.Sites)
{
//Pass web to helper function to enumerate
GetWeb(site.RootWeb);

}
        private void GetWeb(SPWeb web)
{
//TODO: Do something with the web object

//Process the sub webs recursively
foreach (SPWeb subweb in web.Webs)
{
GetWeb(subweb);
}
}

Once you have the web, there are a variety of ways you might find the pages you are interested in.  For example, if it is a publishing site you might want to find all the Pages libraries and get only the published pages -- You could do this with the web object you have available in GetWeb(...):

        foreach (SPList list in web.Lists)
{
if(list.Title == "Pages")
{
foreach (SPListItem item in list.Items)
{
if (item.File.Exists &&
item.File.Url.EndsWith(".aspx") &&
item.Versions[0].Level == SPFileLevel.Published)
{
//Do something clever with the page which is item.File.Url
}
}
}

Note that above we check if the item is published by looking at the level attribute of the version.

Tags: , , , Comments (1) | Permalink
Virtual Earth Map in SharePoint Search Results
16. March 2009 09:15

I often integrate Virtual Earth / Live Maps into MOSS because It's a relatively easy way to add some interesting and useful interaction without any custom code.

I wanted to share with you the 'default' XSL template I typically start from for generating a Virtual Earth Map in my MOSS search results.  From here, I make whatever enhancements or customizations I want, which I'll describe more in a moment.

The prerequisites here are that you have managed columns with Latitude and Longitude that are coming back in your search results (which means you've updated the columns XML to include them on the settings of your search core results webpart)

Here's an example of the managed columns I've got, and will be using -- It's simply an addition of Lat and Lon, and utilization of the existing Title column.  On the right is my updated XML that goes in the columns property of the core results part:

image image

This data could come from a BDC connection to an external data source (e.g. SQL), or you could have a content type or list that has the Lat/Lon information in it.

(If you have a big list or table of data with addresses, and want to generate the coordinates for them, I recommend using this tool: http://www.batchgeocode.com/, You can export your list to excel, save as a delimited format, geocode, and re-import)

So once you have your columns available, this is the base XSL you can put in your core results part to generate a map, and put Push Pins with a popup for the Title at all the correct locations:

 

maptransform.xsl (2.34 kb)

 

The XSL does the following:

  1. Create a div for the map
  2. Includes the Virtual Earth javascript
  3. Write javascript for creating the map, creating a shape array, and pushing the CreateMap function into the body load for sharepoint (_spBodyOnLoadFunctionNames)
  4. Use the XSL to generate a bunch of calls to "Add Shape" based on the search results, to add in all the points.  You could expand this to add any HTML into the pushpin popup, such as content from the result item.

 

You will need to update the Map.LoadMap call to set the zoom level and coordinates to center on (currently its in Redmond, Washington, at a high zoom).

You can build on this using the ability to drag areas of the map to refine search results, or use the built in clustering ability to cluster large groups at different zoom levels.

Good luck!

Tags: , , , , Comments (2) | Permalink
"Tweet This" Link with TinyURL in ASP.Net
14. March 2009 10:59

In migrating to my new blog platform, one thing that I am really excited about is now being able to put custom code to power features I was not able to have in Google Blogger.

The first thing I did was add some code-behind to my user controls to generate good "Tweet This" links so that users could Tweet my posts on Twitter.

 

Most samples you see for this simple stick your page URL in the query string.  This doesn't often work, as the URL will be too long, as the user won't be able to tweet the message.

 

I create two functions behind my page, TweetThisLink and TinyUrl

 

    public string TweetThisLink(string Url, string Title)
{
return "http://twitter.com/home?status=@StefanGordon Reading "
              + TinyUrl(Url) + " " + Title;
}

public string TinyUrl(string Url)
{
try
{
if (!Url.ToLower().StartsWith("http") && !Url.ToLower().StartsWith("ftp"))
{
Url = "http://" + Url;
}
WebRequest request =
                  WebRequest.Create("http://tinyurl.com/api-create.php?url=" + Url);
WebResponse res = request.GetResponse();
string text;
using (StreamReader reader = new StreamReader(res.GetResponseStream()))
{
text = reader.ReadToEnd();
}
return text;
}
catch (Exception)
{
return Url;
}
}

From my ASPX page, I can now insert a nice "Tweet This" link as follows:

<a href="<%=TweetThisLink(Post.PermaLink.ToString(), Server.HtmlEncode(Post.Title)) %>">
Some image or text </a>
Tags: , , Comments (1) | Permalink
Auditing and Compliance in WSS 3.0
29. January 2009 14:29

Auditing from within MOSS (SharePoint Server) is quite simple -- There is a UI for enabling and disabling all of the events you may want to audit.  There is also a facility for viewing and exporting these logs.

However, what if you would like to use auditing features in WSS 3.0 without MOSS?  Believe it or not, most of the same auditing functionality is available, but without a UI.

Most of the information you need to do this is available already on the web, however its very spread out and difficult to digest.  Rather than repeat information that is available elsewhere, let me point you to all your options with some explanations:

You can enable auditing on the list or site level via code -- Here's an example of what that looks like:

SPSite siteCollection = SPContext.Current.Site;
siteCollection.Audit.AuditFlags = SPAuditMaskType.All;
siteCollection.Audit.Update();

You might do this in a feature, or as an application page.



Here is additional information on enabling Auditing for a document library.



Here is information on enabling auditing for a site collection.



You can also enable the auditing without writing any code, using this custom STSADM command from Gary Lapointe.



Then you might ask yourself...



But how do I view my logs?



There is less information available about this.  First, let's talk about where the logs live.



Audit logs are stored in your sites content database, in a table called "AuditData".



That table has a logical schema, and looks like this:



[SiteId] [uniqueidentifier] NOT NULL,
[ItemId] [uniqueidentifier] NOT NULL,
[ItemType] [smallint] NOT NULL,
[UserId] [int] NULL,
[MachineName] [nvarchar](128) NULL,
[MachineIp] [nvarchar](20) NULL,
[DocLocation] [nvarchar](260) NULL,
[LocationType] [tinyint] NULL,
[Occurred] [datetime] NOT NULL,
[Event] [int] NOT NULL,
[EventName] [nvarchar](128) NULL,
[EventSource] [tinyint] NOT NULL,
[SourceName] [nvarchar](256) NULL,
[EventData] [ntext] NULL


You may need to join this data to your Site table via the SiteID if you want to report on it effectively with SQL Reporting Services or even Excel.



Additionally, the Audit API has a more 'proper' way to access the audit data which will be safe in the event that the underlying schema changes in a future update.



Ted Pattison has prepared a sample with the ability to configure the logs and view the log data using the API.



Oh, and of course I plan to add the ability to view audit logs to my SharePoint Log Viewer in a future version!

Tags: , , Comments (2) | Permalink
Programmatically Hide Pages From Navigation When Added To List
8. September 2008 10:17

This has become a fairly frequent request from customers using SharePoint 2007 for Web Content Management. 

Specifically, in a MOSS publishing site, it is requested that links are not automatically added to navigation when the pages are created in the pages library.  Instead, the page should be marked as 'hidden' in the navigation automatically when added to the pages library.  This applies to either Quick Launch, or the Top Navigation (or both).

Typically this is because the governance of the site involves a different person managing the navigation than who may be managing the pages, thus even when a page is added it should not show in the nav until unhidden by another party.

The easiest way I'm aware of to achieve this is with a simple event handler on the pages libraries to hide the page once it is added.

The PublishingPage object will wrap an SPListItem for us, and give us access to the navigation settings.

Here is a simple implementation of this event receiver.  Note that it references Microsoft.SharePoint.dll and Microsoft.SharePoint.Publishing.dll

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;
namespace PageNavigationEvent
{
public class HandlePageAdd : SPItemEventReceiver    
{
public override void ItemAdded(SPItemEventProperties properties)
{
base.ItemAdded(properties);
try
{
this.DisableEventFiring();
if (PublishingPage.IsPublishingPage(properties.ListItem))
{
PublishingPage newPage;
newPage = PublishingPage.GetPublishingPage(properties.ListItem);
newPage.IncludeInCurrentNavigation = false;
newPage.IncludeInGlobalNavigation = false;
}
}
catch
{
// do something clever
}
finally
{
this.EnableEventFiring();
}
}
}
}


 



For initial deployment and testing, I recommend you try out the fantastic Event Receiver Manager which is simple to install and will allow you to graphically add your receiver to a specific pages library without building a feature.



For proper deployment you will want to create a feature, which can install the receiver on all pages lists.  You can find instructions on doing this here.

Tags: , , Comments (1) | Permalink