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)
{...}
}
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've implemented 2 webparts (deriving from Microsoft.SharePoint.WebPartPages.WebPart, the WSS 3 WebPart), one of which is a provider and the other the consumer (implementing ASP.net connection model, with ConnectionProviderAttribute and ConnectionConsumerAttribute methods).
I managed to deploy them in a feature which also deploys a Page Layout containing two webpart zones, which are themselves populated during the FeatureAvtivated method of the feature receiver, with the 2 newly created webparts. All of this works just fine.
For information, I used this link to make it work. Beware, the method using AllUsersWebPart tag in elements.xml, shown in links like this one (http://www.andrewconnell.com/blog/archive/2007/10/07/Having-Default-Web-Parts-in-new-Pages-Based-Off-Page.aspx) work, but if you deactivate, then reactivate your feature, you just have double webparts in your future pages based on the layout. The method described here (http://sharepoint.coultress.com/2008/06/adding-web-part-to-page-layout.html) just threw me an error when analysing metadata for the layout aspx file (the problem seemed to come from the line in the ZoneTemplate tag).
My next goal is to connect these webparts together right after all this, thus enabling the end user to create pages, based on the layout, containing by default the two webparts connected together (right now everything works except for the connected part).
I tried something like this, using ASP.net connection model (the other one, WSS model, throws logically an error because I'm not implementing the good interfaces). But even though the connection resulting from the "mgr.SPConnectWebParts()" method doesn't throw any exception and actually adds the connection to the connection list of the webpart manager, I can see in debug mode that the connection property 'IsActive" is false (maybe normal), and that when I create a new page based on the layout, the webparts appear not connected.
Any guess? I believe there's something with the fact that the webparts cannot be connected before the page containing them is actually created, but I'm far from sure of it.
Declarative web part connection provisioning is actually quite straightforward:
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="Module1">
<File Path="Module1\default.aspx" Url="demo.aspx">
<AllUsersWebPart ID="testProvider">...</AllUsersWebPart>
<AllUsersWebPart ID="testConsumer">...</AllUsersWebPart>
<WebPartConnection ID="testConnection"
ProviderID="testProvider"
ProviderConnectionPointID="providerID"
ConsumerID="testConsumer"
ConsumerConnectionPointID="consumerID" />
</File>
</Module>
</Elements>
Details:
http://blogs.code-counsel.net/Wouter/Lists/Posts/Post.aspx?ID=161
You can find connection point IDs with PowerShell if you first connect your web parts manually:
$web = Get-SPWeb <WebURL>
$wpman = $web.GetLimitedWebPartManager("<PageURL>", [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
$wpman.SPWebPartConnections
Tried creating the web parts on the page programmatically? You'll have far fewer headaches than trying to do it declaratively.
Look up the SPLimitedWebPartManager class for how to handle web parts on a provisioned page.
Also, web parts in a web part zone are tied to the URL of the page on which they are added. This is by design of the the ASP.NET Web Part Manager.
Thus, if you added web parts to zones on a page layout at directory: http://webapp/sites/site/_catalog/master/mypagelayout.aspx - the web parts will ONLY appear on that page. Createa new page at /sites/site/Pages/MyPage.aspx and the web parts you added before won't appear. The workaround for this is to explicitly add web parts not within web part zones, and this can only be done in an authored page layout (usually in SharePoint Designer).
If the web parts are static in the page layout (and you want them to show in every page) then this is actually easier for you to deploy - just maintain the layout in your source, and have it provisioned via a Module element.
Finally I used another approach to reach my goal. In the OnLoad event of the provider webpart, I check if my page is in edit/new mode, and then check if the page contains the consumer webpart (via the webpartmanager) and if they are not already connected. If this is the case, I connect them.
The code to connect permanently the webparts:
private void SetUpConnections()
{
SPSecurity.RunWithElevatedPrivileges(delegate() {
using (SPSite siteContext = new SPSite(SPContext.Current.Site.ID))
using (SPWeb webContext = siteContext.OpenWeb(siteContext.ServerRelativeUrl))
using (SPLimitedWebPartManager spManager = webContext.GetFile(SPContext.Current.File.Url).GetLimitedWebPartManager(PersonalizationScope.Shared))
{
foreach (Microsoft.SharePoint.WebPartPages.WebPart consumer in spManager.WebParts)
{
if (consumer is MyConsumerWebPart)
{
bool alreadyConnected = false;
Microsoft.SharePoint.WebPartPages.WebPart provider = spManager.WebParts[this.ID] as Microsoft.SharePoint.WebPartPages.WebPart;
foreach (SPWebPartConnection connection in spManager.SPWebPartConnections)
{
if (connection.Provider == provider && connection.Consumer == consumer) { alreadyConnected = true; break; }
}
if (!alreadyConnected)
{
// Connects webparts permanently (but the page would need a reload to display the connection)
ProviderConnectionPoint providerConnectionPoint = spManager.GetProviderConnectionPoints(provider)["MyConnectionProviderInterfaceId"];
ConsumerConnectionPoint consumerConnectionPoint = spManager.GetConsumerConnectionPoints(consumer)["MyConnectionConsumerInterfaceId"];
spManager.SPConnectWebParts(provider, providerConnectionPoint, consumer, consumerConnectionPoint);
// Connects webparts locally (for current edit mode)
SPWebPartManager currentSPManager = WebPartManager.GetCurrentWebPartManager(this.Page) as SPWebPartManager;
System.Web.UI.WebControls.WebParts.WebPart currentProvider = this;
System.Web.UI.WebControls.WebParts.WebPart currentConsumer = currentSPManager.WebParts[consumer.ID];
ProviderConnectionPoint currentProviderConnectionPoint = currentSPManager.GetProviderConnectionPoints(currentProvider)["SearchBarProvider"];
ConsumerConnectionPoint currentConsumerConnectionPoint = currentSPManager.GetConsumerConnectionPoints(currentConsumer)["SearchBarConsumer"];
currentSPManager.SPConnectWebParts(currentProvider, currentProviderConnectionPoint, currentConsumer, currentConsumerConnectionPoint);
}
}
}
}
});
}
The code to check if the page is in new/edit mode:
if (SPContext.Current.FormContext.FormMode == SPControlMode.New
|| SPContext.Current.FormContext.FormMode == SPControlMode.Edit)
{
this.SetUpConnections();
}
I have MOSS server and users authorization is going trough AD.
I want to programmaticaly get some current user information, like e-mail, phome number.
I made the following steps:
1) create dll with [assembly: AllowPartiallyTrustedCallers]
2) i have the class PhoneBookCL that inherits System.Web.UI.WebControls.WebParts.WebPart
and I try to override CreateChildControls() for testing;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.Office.Server;
using Microsoft.Office.Server.UserProfiles;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Portal;
public class PhoneBookCL : WebPart
{
SPUser currentUser = null;
protected override void CreateChildControls()
{
try
{
SPWeb web = SPControl.GetContextWeb(Context);
currentUser = web.CurrentUser;
}
catch (Exception exp) { value = exp.Message; }
}
}
3) assigned strong name to dll, then add SafeControl to web.config:
4) dll -> to \Bin directory, added this WebPart to SP webparts collection,
added the webpart to the new Page.
Refreshing the page going to the exception: unexpected exception.
If I delete
"SPWeb web = SPControl.GetContextWeb(Context);
currentUser = web.CurrentUser;"
then all works great.
I can create Label and change its Text property and other things.
Also I tried to inherit from Microsoft.SharePoint.WebControls.WebPart, which actually inherit UI...WebPart, so the result is the same.
Maybe there is some security issue?
This problem occurs as you are deploying dll to bin. I assume your web.config has trust level as WSS_minimal . Minimal trust will not permit accessing object model.
Therefore , you have the following options:
Deploy the webpart using a wsp with custom code access security. This is slightly tedious.
Change the trust level in web.config to Full.
Deploy the dll to GAC.
From a best practices perspective, deploying the dll to bin with custom code access security will be the best option. But if you dont have security restrictions, you can look at deploying to GAC to keep things simpler.
Reference : http://msdn.microsoft.com/en-us/library/ee909485%28office.12%29.aspx
Regards,
Faiz
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.
I have some code that creates a new site in SharePoint. Upon browsing to the newly created site, a File Not Found error is thrown. If you browse to /_layouts/ChangeSiteMasterPage.aspx you can select a new site master & system master page, and the site works fine. This kb article describes my symptoms perfectly: http://support.microsoft.com/kb/936908
My problem is that of the two (maybe three?) solutions given, only one works. If I manually select the new master pages it works fine. The second workaround is to activate the publishing feature on the new site. This does not fix anything.
There is also a recommendation to staple the publishing feature to the site definition I am using for the new site. In my case, this is STS#1 (the blank site), and stapling the publishing feature does not alleviate my problem.
Anyone have an idea of how I can get the correct master page sorted out?
We do this through a delegate control. In the OnLoad we call the following method:
private void ConfigureMasterPage(SPWeb web)
{
string masterURL = string.Empty;
masterURL = web.Site.ServerRelativeUrl + "/_catalogs/masterpage/XXX.master";
masterURL = masterURL.Replace("//", "/");
web.MasterUrl = masterURL;
web.CustomMasterUrl = masterURL;
web.Update();
}
Dont forget to do a dispose of the SPSIte and SPWeb objects and You will have to set AllowUnsafeUpdates to true.
Hope this helps
Turns out I just needed to change the master template in one of the generated sites and then save that as a template. I deleted the broken template and put this in its place. Now all the generated sites work fine.