Registering an event handler for a single list - sharepoint

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.

Related

Issue while Instantiating SharePoint 2010 method in MSCRM 2011 plug-in

This is regarding SharePoint 2010 Integration with MSCRM 2011.
While creating a record in CRM, trying to create a Custom Document location for that record and a similar folder in sharepoint, So that when user clicks on document link in the entity record it does not prompt user to create folder in Sharpoint (Trying to avoid sharepoint noise for better user experience)
I have implemented through post create asynchronous plug-in. (I did this through console program working fine). Build the plugenter code here-in and deployed to CRM.
When creating a record it error out with a message like "An internal server 500 error - Could not load the assembly with public key token etc…blab bla bla…”
But when I am debugging the plug-in it failed at the first line of command where I am instantiating sharePoint method Create client context of sharepoint, it says [System.Security.SecurityException]={“That assembly does not allow partially trusted callers”.}
As per google, per this issue it should be having one attribute “Allow partial users” in assembly info file. As per my understanding, this should be done in because the request goes from CRM plug-in to SharePoint dll. I mean share point dlls are not allowing request from my assembly. How can we change that?
I have referenced Microsoft.SharePoint.client.dll and Microsoft.SharePoint.Client.Runtime.dll
What is the alternate to overcome this issue?
Appreciate if some one can help me ..Thanks In advance.
Here is my code for SharePoint
ClientContext clientContext = new ClientContext(siteUrl)
CredentialCache cc = new CredentialCache();
Cc.Add(new Uri(siteUrl), "NTLM", CredentialCache.DefaultNetworkCredentials);
clientContext.Credentials = cc;
clientContext.AuthenticationMode = ClientAuthenticationMode.Default;
Web web = clientContext.Web;
SP.List list = web.Lists.GetByTitle(listName);
ListItemCreationInformation newItem = new ListItemCreationInformation();
newItem.UnderlyingObjectType = FileSystemObjectType.Folder;
newItem.FolderUrl = siteUrl + "/" + folderlogicalName;
if (!relativePath.Equals(string.Empty))
newItem.FolderUrl += "/" + relativePath;
newItem.LeafName = newfolderName;
SP.ListItem item = list.AddItem(newItem);
item.Update();
clientContext.ExecuteQuery();
Where I am passing the siteurl, folderlogicalname,relativepath and new foldername as parameters.
This works fine from my Console application. But when converted to CRM plug-in it gives the above specified issue
I've seen a similar issue before.
CRM plugins run inside a sandbox, so all assemblies and .NET libraries used must allow partial trust callers (since the CRM sandbox runs under partial trust). It works in the console because you are executing the code as a full trust user in that context.
This issue is not necessarily your code, but could be a dependency or a .NET library itself does not allow partial trust callers - in your case it sounds like the Sharepoint library is the culprit (but a stack trace of the error should reveal exactly where the cause is).
Since you don't have access to the source library causing the problem, to overcome the error you will likely have to create a wrapper. However, the problem is the wrapper cannot directly reference the problem library or you will get the same issue. So to get around this, you may have to create a web service which acts as your wrapper and then call the web service in your CRM plugin. This way the full trust code is executed by the web service (which is full trust) and then returns the result to your calling CRM plugin.
Here is more info on the error.
Thanks Jason. This works for me.
I Would like to add additional few points to the answer.
1. I have added the sharepoint dlls to the bin folder of CRM 2011 site.
2. Also deployed the same dlls in the folder whereever Async job is running to make my Async plug-in to work.
Thanks once again for the cooperation

feature versioning in sharepoint

I've followed following link to implement feature versioning:
http://sisharepoint.wordpress.com/2010/01/21/using-the-featureupgrading-event-to-upgrade-features-sharepoint-2010/
I am new to sharepoint and the requirement is to show the versions of features in my site. Is it possible?I am not able to see the version anywhere in the site. I can see appropriate version in the feature.xml file in feature folder of 14 hive. Just want to know that is it possible to see the versions of each deploy in sharepoint site also?If yes then where can I see it?
Thanks,
Priya
If custom solution fits your requirement then you can try following ways to find activated feature versions.
Use SPFarm.FeatureDefinitions
to get all activated features in the Farm -
SPFeatureDefinitionCollection farmFeatures = SPFarm.Local.FeatureDefinitions;
foreach (SPFeatureDefinition feature in farmFeatures)
{
....
}
To find a version of a particular feature
var spFarm = SPFarm.Local;
System.Version version = spFarm.FeatureDefinitions["YourFeatureName"].Version;
Use SPContext.Current.SiteFeatures or SPContext.Current.Site.Features
var siteFeatures= SPContext.Current.SiteFeatures;
foreach (SPFeature sf in siteFeatures)
{
variable = sf.Definition.DisplayName;
variable = sf.Definition.Version.ToString();
}
4 Use SPContext.Current.WebFeatures or SPContext.Current.Web.Features
var webFeatures= SPContext.Current.WebFeatures;
foreach (SPFeature webFtr in webFeatures)
{
variable= webFtr.Definition.DisplayName;
variable= webFtr.Definition.Version.ToString();
}
Hope this helps.
There's no way to see this in Central Admin or Site Settings. The point is to abstract away versioning from users. Users just know that a specific feature is available, not what version. I agree that it would be nice to actually be able to see this info without having to write a custom solution.

SPSite site = new SPSite(SPContext.Current.Web.Url) vs SPContext.Current.Web.Site

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
}
}
});

Versioning by default in SharePoint

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);
}
}

SharePoint Development Permissions

Hi I am using the SharePoint namespace to pull items from various lists throughout the site. My web part works, but only on my account. When I try it on another account it gives me "Error: Access Denied" for the page. I have taken all web parts out and have only this web part on the page. When I remove the following lines the page loads for everyone, when I add it back in however it does not work. I am guessing this is some permission problem. I was wondering is there away to programatically query different lists on SharePoint by assigning a user id to use? Thank you for any help
...
SPSite site = new SPSite(_SPSite);
SPWeb eachWeb = site.AllWebs[0];
SPListItemCollection myItemCollection = eachWeb.Lists["Listings"].Items;
...
You're correct, the access denied error is occurring when you're using an account which does not have access to the "Listings" list in the current website.
The easiest way around the issue is to use a SPSecurity.RunWithElevatedPrivleges call:
SPSecurity.RunWithElevatedPrivleges(delegate()
{
//Your code here
});
which will run whatever code is contained in the anonymous method using the SharePoint/System account, granting complete control. Be careful when using this technique though, as it equivalent to running code at full trust with a super user account. There are other caveats to be aware of as well.
Try:
SPWeb eachWeb = SPContext.Current.Site.RootWeb.Webs[0];
SPListItemCollection myItemCollection = eachWeb.Lists["Listings"].Items;
Remember that SPWeb should be used in a using block, or disposed of explicitly after use.
As regards the first caveat from EvilGoatBob, I quote:
"If you're manipulating any Object Model elements within your elevated method, you need to get a fresh SPSite reference inside this call. For example
SPSecurity.RunWithElevatedPrivileges(delegate(){
SPSite mySite = new SPSite(http://sharepoint/);
SPWeb myWeb = SPSite.OpenWeb();
// further implementation omitted
});"
Notice that the site parameter is hard-coded - this is because of a bug. If you instead had tried:
using (SPSite site = new SPSite("http://" + System.Environment.MachineName)) {}
You would get the rather generic "No SharePoint Site exists at the specified URL..." error. This caused me no end of grief. Bottom line is that you have to hard-code the server name (unless anyone has an alternative). You can also get a similar error message when debugging Web Parts for the first time with VSeWSS 1.3.
You do not need to hardcode the server name in this case because your requirement is to retrieve items from list inside the same site as your webpart. You are correct, if you do not have enough privileges with your account, then you get the Access Denied. The solution is to create a new SPSite object within a different security context, and do your work:
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite(SPContext.Current.Site.Url))
{
using (SPWeb web = site.OpenWeb())
{
//the web object was retrieved with elevated privileges under the system account.
//do your work here:
SPListItemCollection myItemCollection = web.Lists["Listings"].Items;
//...
}
}
}
);
With the code above, your webpart is portable because there's no hardcoding, and runs in the correct security context while disposing of all unmanaged SPRequest objects created by the SPSite and SPWeb constructors.

Resources