If you didn’t read Dan’s post then you have a second chance by reading this one .. and if you already did read his post.. just read this one again ;)
Basically it all comes down to.. If you love SharePoint (its all we do), care about the community (blog, twitter, tools), want to create innovative solutions for great clients and want to be part of a team that is committed to making consulting the high value profession it once was..
I think I speak for the whole zevenseas team when I say that we absolutely love Mondays and Fridays.
On Monday mornings we get to work with some of the worlds biggest and best companies, on projects that push collaboration and SharePoint to the limits. Friday mornings start with breakfast at our local cafe, time where we can share our week over coffee, learning and solving each others problems as we go. We then head to the office where we spend the rest of the day trying to impress each other with our latest demos or get to work on our next cool tool.
Making sure we enjoy what we do was one of the most important goals we set ourselves when we began this boutique consultancy way back in Jan 2008. We have succeed by focusing not just on the Mondays and Fridays, but by taking a long term view. We all have the best equipment, we have the time to share and build knowledge, and through conference attendance and community engagement, we’ve built a network of experts that help us to stay up to date.
I have to say, our first year has gone better than we could ever have expected, except for one thing. We would really like to (carefully) grow our team here in the Netherlands.
Its time you started loving Mondays and Fridays too (and of course all the days in between).
Just got an email a week ago with some questions about the updated LCM tool and I wanted to share the questions and answers with you ;)
- Q: How does it determine the site owners? Does it go through the group? What if you have no groups, just permissions of "full control"?
- A: It checks for all users with the full control permission mask
- Q: How resource intensive is this process?
- A: Depends on the size of your farm really. I've created a timerjob especially for this to let it run in the background. So it shouldn't really matter but it is recommended to let it run in non production hours
- Q: On site deletion, we noticed that the list entry for the delete action had an extra .bak in the file path, was that our misconfiguration
- A: With the configuration of the path, please be sure to end the pathname with an '\' character. Other than that it should be outputted correctly.
Technorati Tags:
SharePoint,
LCM0 Comments
|
Posted February 20, 2009 - 13:41
by Robin Meure
in
This post is about why it took me so long to get the updated LCM version on CodePlex ;)
- Redesign, redesign and redesign. Well.. as in all applications that you built there is always a point where you decide to redesign and refactor your code and during the development of the LCM, I’ve had a lot of new insights. Mainly due the fact I was building this thing was in my spare time so I was taking all my experience from the ‘daily work’ into the project. So here are some examples of finding new stuff to reuse straight out of the SharePoint toolbox.
- Classes
- (SPPersistedObject) One of the first things I was trying to do was using the PropertyBag of Central Administration webapplication to store settings in. Now this all went fine for the gathering of sitecollections on all webapplications if the timerjob was being set to the Central Admin webapp itself. But it didn’t work for getting the configuration of the SiteCapture Deletion settings when a site or a web got deleted.
Why not? Well it is good practice that every web application should have it’s own application pool account. So while running with elevated privileges you will be impersonated to the application pool account of the particular web application where the site or webs get deleted. And since that account is different then the one from the Central Admin webapp, the propertybag does not meet the requirement. So what does then? As you might have guessed.. it’s the SPPersistedObject Class. Why? Well, using this class you can save properties to the given Web Application you are configuring and therefore the application pool account has access to it. Next to this, it’s very easy using this object to have different settings per Web Application ;)
- Controls
- (WebApplicationSelector) Since the stapling of the SiteDeletion Capture is only stapled to new sites and not the existing sites I created a simple function to enable the feature on all the webs and sitecollections on a given web application. To select a web application I used a standard .NET dropdownlist that featured all the webapplications of the current farm. And while looking at out-of-the-box application (like the Site Collection List page) I noticed Microsoft used a control called the WebApplicationSelector which does everything for you. One of the most useful features is that it keeps the context of the selected Web Application consistent in every page where the control is being used.
- (SchedulePicker) To schedule the timerjobs I first used two standard .NET dropdownlists. One had all the all the days of the week and the other had every hour from a day. But I didn’t find it very ideal and I started browsing the SharePoint pages again to see what MS uses to set schedules. And from there I found the SchedulePicker class in the ControlTemplates folder (it’s actually being used in the page where you define the schedule of the WSS search). I explicitly didn’t use any MOSS specific controls to ensure that the solution can be used in every SharePoint environment (though that control is much better looking ;)
- I also wanted to check on MOSS Usage Details.. Was I in for a surprise when I found out that simple things just as checking if the Usage Analysis Processing was enabled in the SSP was just not available using the OM! I still don’t really understand why the SharedResourceProvider class is internal and sealed. It makes things very complicated when you want to do something with the SSP (like determining the default SSP.. )
Using Reflector I found out that the usage reports that you see, when you enable the MOSS Usage Reporting functionality, that the report is generated using Stored Procedures that are stored in the SSP Database. Using the GUID of each sitecollection as a parameter for the stored procedure I’m able to retrieve all the processed usage data.
But(!) since it’s very unsupported to access stored procedures/tables/etc directly from the SharePoint databases I decided not to implement/hack my way around to get it.. (yet…;)
- Testing, testing a tool like the LCM is very difficult. I don’t know how your VPC looks like but mine hasn’t got a lot of sitecollections and webs that are ‘used’ as in a typical SharePoint production environment. Nor the fact that my VPC is up and running 24/7 in order to let the Usage Analysis Process to be processing.. But when you test things out, you come across some interesting things like when you change or set a lockissue on a sitecollection you are also updating the ‘lastcontentmodifieddate’. And one of the things I check whether or not a sitecollection is being used is by checking that property! ;)
- Changing my mind all the time.. I think this was the largest time consumer of them all to be honest ;)
One example : “ Should I only check sitecollections or should I also check for webs.. If I also check on webs, how do I deal with the whole locking mechanism then? And where to store the data.. Should I use two lists, working out a master detail relationship? Or should I store in a SPPersistedObject ? What is the limit of the SPPersistedObject ? “
So there you have my excuse that I took my almost a year to get it out there..
0 Comments
|
Posted February 17, 2009 - 22:16
by Robin Meure
in
After blogging about a year ago Site Life Cycle Management V2 .. Why V2- I’ve finally come around and make it ‘BETA’ –able enough to put it on CodePlex for you to try out (and hopefully use ;)
So what does it do?
- Captures sitedeletions and makes a backup first before deleting the site or web.
- Gathers sites that are unused (using the Usage info, Last Content Modified Date and Last Security Modified Date)
- Actions can be taken on this "unused" sites, such actions are
- Mail the siteowner and informing them that their site is being unused
- Lock the site after a given period of time
- Delete the site after a given period time
Please review and test this application properly. Do not just install this application on your production environment. This solution is still in beta phase Installation
Just click on setup.exe and make sure it installs onto your Central Administration Web Application. After the installation go to “Manage farm features” in the “Operations” tab and activate the following features
• Lifecycle Management Delete Capture Stapling
• Lifecycle Management UI
The first feature ensures that the feature receiver that captures the deletion of a web or a site is stapled onto all the existing out-of-the-box site templates. Please note: all your existing sites don’t have the feature receiver attached yet. This must be done using the admin UI. Which brings us to the second feature that is there to make all the administration links appear in the “LifeCycle Management” in the “Application Management” tab like so :
Configuration
Site deletion Capture Settings
- First select the Web Application which you want to configure.
- Next you can install the event handler on the selected Web Application so that all the existing sites will get the capture functionality as well. Alternatively you can check whether the event handler is installed already.
- Next you can define the path where to backup up to when a site or web gets deleted
- You can check whether or not to include a datetime stamp within the filename
- You can check to create a subfolder per sitecollection
- You can check to create a subfolder per web ((and if the above checkbox is checked) per sitecollection)
- You can check to log all the deletions in SharePoint (please note: this only works when the LCM site is created using the “Timerjob configuration” administration page)
Timerjob Configuration
- Choose the Web Application which you want to configure
- Define when a site should be marked as unused, by default this is set to 90 days. You have to option to check on three different kind of date that are tracked by SharePoint
- Last Content Modified Date, this is the date when for the last time a particular content item (listitem, document, etc) was modified.
- Last Security Modified Date, this is the date when for the last time something has changed in the security settings.
- Last Usage Date, this is the date when an user for the last time has visited the site. (Please note : this is only available if you have “Analysis Processing” option enabled. And won’t go back further then one month)
- The first “Action” to do something about those unused sites is to mail the siteowner of that particular site. Check “Mail siteowner before locking/deleting” to make this happen.
- Define how many days the system must wait to advance to the next ‘Action’ level (eg, locking or deletion of the site)
- Define the subject of the mail that is send
- Define the body of the mail that is send
- The second “Action” is locking the site. The type of locked that is used in this version is the ‘writelock’. This lock prevents the user to add or modify anything on the site.
- Define how many days the system must wait to advance to the next ‘Action’ level (eg. deletion of the site or do nothing at all)
- The third “Action” is the deletion of the site. I guess this action speaks for itself ;)
Timerjob Scheduling
After all the configuration you can choose to schedule a timerjob that will loop through all the sites at a given day and time.
We have two different timerjobs :
- Statistics Gathering Timer Job Schedule , this timerjob is responsible for gathering all the data about the sites
- Maintenance Timer Job Schedule, this timerjob is responsible for taking the actions on the gathered sites that are configured in the previous page
Important! As can be read in the page, it’s very wise to schedule this timerjobs at off-peak hours. Since it can impact the performance of your farm.
Unused Sites Overview
This page is also known as the ‘Manual’ LifeCycleManagement page, meaning that you can do the ‘automated’ by hand ;)
- Mail, the selected site owner will be mailed using the mail template that is configured for the given Web Application. (Please note: this option is only available if you have a mail server are configured in your farm)
- Lock, the selected site will be write locked.
- Delete, the selected site will be deleted (Please note : make sure that the WebApplication is configured for capturing the deletion since this action is not responsible for creating a backup first)
- Gather, an one-time scheduled timerjob will be initiated and all the sites of the given WebApplication will be checked and marked as unused as how the Web Application is configured)
- Action, also an one-time scheduled timerjob will be initiated to perform the configured actions.
Deleted Sites Overview
A simple overview page that shows you all the captured sites. This page is not really worth a picture ;)
Wishlist
My current wishlist/todo list is as follows:
- Support for gathering webs and not only sitecollections. The code is already there but I have to create some UI and logic around it.
- Add variables for the emails
- Better reports about what is going on with the timerjobs
So there you have it again.. please let me know what you think of it. And please remember this is still a beta product so don’t put in production straightaway :)
Oh yeah.. almost forgot, get it here : http://www.codeplex.com/zsLCM
9 Comments
|
Posted February 6, 2009 - 16:40
by Robin Meure
in
In addition to my previous post about this wonderful object, be careful what Type you use to ‘persist’. Please be sure that you don’t use any ‘complex’ types such as a DataTable or UserPrincipal. Otherwise during the storing itself, the following error will be raised:
The platform does not know how to deserialize an object of type System.Data.DataTable. The platform can deserialize primitive types such as strings, integers, and GUIDs; other SPPersistedObjects or SPAutoserializingObjects; or collections of any of the above. Consider redesigning your objects to store values in one of these supported formats, or contact your software vendor for support.
The bad thing about this that you have to know exactly where you have stored (or tried to store ;) the object in order to Unprovision and Uncache and after that Delete the object.
Since I couldn’t find any reference of the object it costed me my config database since when retracting my solution that had this custom SPPersistedObject, it failed. So I couldn’t remove my solution anymore.. even worse.. the feature couldn’t be deactivated as well since SharePoint does some calls to the config database and tries to deserialize the object while it can’t and there will fail.
Technorati Tags:
SharePoint1 Comments
|
Posted February 6, 2009 - 11:57
by Robin Meure
in
At first I thought I didn’t look good when I was looking for the Url property on the SPList class because it’s child and parent classes has them (SPWeb, SPListItem, SPFile) and thus, so I thought, the class itself should have it as well. But.. it doesnt! Aaargh.. Which properties are there to use then to you might wonder? Well first let me explain why I want to have the Url property. I’m trying to replace the default ListFormWebPart on the editform.aspx pages on certain lists and/or document libraries. To do this I use the SPLimitedWebPartManager and this thing requires a server-relative or absolute url to the page are you trying to access.
So at my first go I tried the following bit :
SPLimitedWebPartManager webPartManager =
web.GetLimitedWebPartManager(web.Url + "/lists/" + list.Title + "/editform.aspx",
System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
But since the title of a list could be very different from the actual Url this was not really an option so I had to look for other properties and found the following:
- RootFolder (thanks Ton :)
- DefaultViewUrl
- Some string manipulation at first and then you have this beautifully crafted server-relative url ;)
string listUrl = list.DefaultViewUrl;
int index = listUrl.IndexOf("Forms") + 6;
listUrl = listUrl.Remove(index);
if (listUrl.EndsWith("/"))
{
listUrl = listUrl.Remove(listUrl.LastIndexOf("/"));
}
SPLimitedWebPartManager webPartManager =
web.GetLimitedWebPartManager(listUrl + "/editform.aspx",
System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
Taking this a step for further and using the technique that Adam Buenz blogged about at Writing Extension Methods for SharePoint it’s incorporated in the SPList class like so:

public static class Extensions
{
/// <summary>
/// Returns a server relative url of the list
/// </summary>
/// <param name="typeToTarget"></param>
/// <returns>string</returns>
public static string Url(this SPList typeToTarget)
{
string listUrl = typeToTarget.DefaultViewUrl;
if (typeToTarget is SPDocumentLibrary)
{
int index = listUrl.IndexOf("Forms") + 6;
listUrl = listUrl.Remove(index);
if (listUrl.EndsWith("/"))
{
listUrl = listUrl.Remove(listUrl.LastIndexOf("/"));
}
}
else
{
int indexSlash = listUrl.LastIndexOf("/");
listUrl = listUrl.Remove(indexSlash);
}
return listUrl;
}
}
And the end result being :
SPList list = SPContext.Current.Web.Lists[new Guid(id)];
SPLimitedWebPartManager webPartManager =
web.GetLimitedWebPartManager(list.Url + "/editform.aspx",
System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
6 Comments
|
Posted February 1, 2009 - 15:45
by Robin Meure
in