How can I modify a SharePoint site so that versioning is turned on by default in Document Libraries?
Versioning is not done at the site level, but at the list level.
If you want versioning to be turn on on each new library, you'll have to either:
Use your own library template (with versioning turned on)
Use feature + event handler to programmatically activate versioning on each new list
The easiest way is probably to use your own template. To do this, create a new document library, activate versioning, then save this list as template.
When you create a new list, you will then be able to use your template and directly create a list with versioning activated.
You could of course create your own site definition, but that's probably not the best solution. Creating a custom library template will work too, but if you want versioning turned on for the libraries that a particular site definition creates for you, you'll have to come up with something else.
We happen to have done this for our SharePoint implementation. We decided the best way was to create an event handler feature and staple it to all sites so that when the site is created, versioning will get turned on for all existing document libraries. Of course, new document libraries would get whatever versioning options the user who created it set.
The problem we ran into is that there is no "ListCreating" event handler so we couldn't turn the versioning on at that point. So, we tried to put the code inside the FeatureActivated event handler, figuring it would be activated on site creation and then all document libraries could be changed to have versioning turned on. The problem is that this event fired before the libraries were actually created.
So instead, we decided to put the code into the "ItemAdding" event handler and remove it after the first time that it runs. So the first time a user adds a list item or a document, it will turn on versioning for all document libraries in the site. This way, we ensure there is no way for a user to add a document to an existing library without it being versioned. Additionally, any libraries that get created before an item gets added will have versioning turned on by default as well.
It was a bit of a hairy solution, but it has worked very well for us. Here's the code we used:
public class SetVersioning : SPItemEventReceiver
{
public override void ItemAdding(SPItemEventProperties properties)
{
SPWeb CurrentWeb = properties.OpenWeb();
foreach (SPDocumentLibrary doclib in CurrentWeb.GetListsOfType(SPBaseType.DocumentLibrary))
{
doclib.EnableVersioning = true;
doclib.MajorVersionLimit = 8;
//doclib.EnableMinorVersions = true;
doclib.Update();
}
//now get rid of the receiver
SPEventReceiverDefinitionCollection receivers = CurrentWeb.EventReceivers;
foreach (SPEventReceiverDefinition definition in receivers)
{
if (definition.Name.Equals(EVENT_RECEIVER_NAME))
{
definition.Delete();
break;
}
}
base.ItemAdding(properties);
}
}
Related
Why do some SharePoint examples use
using (SPSite site = new SPSite(SPContext.Current.Web.Url))
{
...
}
and not just simply?
SPSite site = SPContext.Current.Web.Site;
...
Update
I think I have narrowed the question down to the following:
It seems that I should not use SPContent.Current directly, unless I am certain, that my code runs inside SharePoint. But when would that not be true?
Take a look at the best practices documentation on disposing objects in SharePoint 2010 from Microsoft, however there are opposing views.
There are a few key takeaways for SharePoint projects:
Always dispose your SPWeb / SPSite objects --> memory leaks
Make use of SPContext.Current... when you are sure your code is running in a SharePoint context
Unit Tests mean no Sharepoint context
External utilities mean no Sharepoint context
Powershell means no SharePoint context (e.g. activating a feature with feature receiver might fail)
Do not dispose SPContext.Current... but create your own object (again using)
You might have problems with consistency with your multiple SP.. objects.
In the end SPSite site = SPContext.Current.Web.Site; is fine in some instances, but you do not have control over this site object - that might be the problem. If you go for new SPSite(...) you will always have your SPSite and not something SharePoint created and managed for you.
Personally I almost always go for the using structure so all objects are disposed properly afterwards. Alternatively I use SPContext.Current.Web without disposing.
It depends on the context in which your code runs. For instance, you need to create a new SPSite instance if you are running within a RunWithElevatedPrivileges block.
Dennis G is correct. Disposing the SPSite/SPWeb/etc is important but make sure you do not dispose the objects that are provided to you by the API directly. It's subtle but critical otherwise your response will never get generated or cause even thread abort situations.
In my experience, if I need quick information on the SPSite or SPWeb property that I am sure available to the user context (either a content manager authorized user or anonymous), then using SPContext.Current.* object is great. Otherwise, use the RunWithElevatedPriveleges method to wrap your code and inside that lambda has the following pattern:
SPSecurity.RunWithElevatedPrivileges(() =>
{
using (SPSite site = new SPSite(SPContext.Current.Site.ID))
{
using (SPWeb web = site.OpenWeb(SPContext.Current.Web.ID))
{
// stuff goes here elevated
}
}
});
I have a MasterPage that I am deploying to a SharePoint 2007 server. I am using a feature and a wsp to do the deployment. After deployment, my new masterpage isn't available to select and use for my site. Then, if I activate my feature, I am able to select my master page. But, when I deactivate my feature (or even retract the solution and delete it from SharePoint), the master page is still available for selection and all of the other files that were part of my feature/solution are still on SharePoint. So, is there any way to remove the master page from being available when my feature is deactivated, and then if it gets activated again, have it be available again?
Hope this makes sense, thanks.
SharePoint doesn't by default clean up files deployed as part of a feature activation.
In order to remove the master page and other associated files you'll need to write a feature receiver for your feature, implement the FeatureDeactivating method, and remove your files using object model code instead of CAML. MSDN document for feature receivers is here, and there are blog examples of writing feature receiver code all over the web.
Bear in mind that in order to remove your master page you'll first need to make sure you reset the master page for all sites in the site collection to the default/another available master page. You'll also want to be careful not to remove resource files (CSS, images, etc.) that are shared among master pages or page layouts.
First make sure you are not using the Master page anymore in the feature deactivating. Then you can remove it.
SPWeb web = (SPWeb)properties.Feature.Parent;
string customMasterUrl = (new Uri(web.Url + "/_catalogs/masterpage/Sample.master")).AbsolutePath;
if (web.MasterUrl != customMasterUrl)
{
try
{
SPFile file = web.GetFile(customMasterUrl);
SPFolder masterPageGallery = file.ParentFolder;
SPFolder temp = masterPageGallery.SubFolders.Add("Temp");
file.MoveTo(temp.Url + "/" + file.Name);
temp.Delete();
}
catch (ArgumentException)
{
return;
}
}
I've added a feature to my onet.xml file which gets activated whenever a site gets created. However, that feature needs to know the url of the site being created. I thought I could figure that out from the current SPContext within the activation event of the feature, but when I created the site I got a null reference on SPContext.Current.
Is that to be expected, or have I done something wrong? If that is the case, does anyone have any suggestions how I can dynamically learn the URL of the site being created?
Thanks
It seems like you have created a feature receiver? They don't use SPContext but find the site they have been activated on through the properties, like so:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
using (SPWeb web = properties.Feature.Parent as SPWeb)
{...}
}
I want to start logging some custom messages into the ULS from my custom SharePoint code. My code is running inside list item receivers attached to some lists. I'd like to configure this logging mechanism within the application start event handler in global.asax. What's the best-practices way to deploy a SharePoint solution package that modifies global.asax?
I don't know about "best practice", but I would be quite keen on making the edits via code in the feature reciever.
With a line that backs up the file for later restoration.
For logging, we have used Scott hilliers code here to create a trace provider to log with.
Works a treat.
I should clarify that we use a static readonly wrapper for the trace provider
static readonly Log instance = new Log();
that registers itself with the code
SPFarm farm = SPFarm.Local;
Guid traceGuid = farm.TraceSessionGuid;
unit result = NativeMethods.RegisterTraceGuids(ControlCallback, null, ref traceGuid, 0, IntPrt.Zero, null, null, out hTraceReg);
System.Diagnostics.Debug.Assert(result==NativeMethods.ERROR_SUCCESS, "TraceRegister result = " + result.ToString());
Gah!
This then gets instanciated only once and we use the destructor to unregister it.
I have a sharepoint event handler which I want to activate for a single list, not all the lists in the site. How do I go about this?
Got the answer. We need to run this code, maybe in a console app. I still didn't get how to remove the event handler once it has been added though...
string siteUrl = Console.ReadLine();
SPSite site = new SPSite(siteUrl);
SPWeb web = site.OpenWeb();
string listName = Console.ReadLine();
SPList list = web.Lists[listName];
string assemblyName = "Issue.EventHandler, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=89fde668234f6b1d";
string className = "Issue.EventHandler.IssueEventHandler";
list.EventReceivers.Add(SPEventReceiverType.ItemUpdated, assemblyName, className);
Just that list or that list in each site ?
I have been testing the code that run when the event happens and I have used a nice little tool from u2u, which allows me to add or remove event handlers per list.
This MSDN article is a nice primer.
Another alternative is the "SharePoint Events Manager".
Events Manager is a Feature for SharePoint that allows administrators to manage events attached to their site's lists and document libraries directly using their browser.
This simple feature enables management of events on lists and document libraries through a new item on list settings menu.
You can view, add and delete events, and even find automatically interesting classes and events from an assembly name.
You can download this feature here, and install it using "stsadm -o addsolution -filename GatWeb.SharePoint.EventsManager.wsp".
This feature is localized in french and english.
I recently gave a talk at our Sharepoint SIG about this very problem. The slides and tools are available here.
You can
write a console app to do this
write a features that uses the code in your console app to deploy to the proper list
use PowerShell
use Brian Wilson's admin tool
You can use this code for removing event handlers:
for (int i = 0; i < olist.EventReceivers.Count; i++) {
olist.EventReceivers[i].Delete();
}
Take a look at the code that comes with the tool from u2u that I posted earlier. It is a convenient tool when you are working with event handlers.