Why is this considered an "Unsafe Update" in Sharepoint? - get

I have code like this to conditionally create a Sharepoint list (sort of an upsert):
private void ConditionallyCreateList()
{
SPWeb site = SPContext.Current.Web;
// Check to see if list already exists; if so, exit
if (site.Lists.TryGetList(listTitle) != null) return;
SPListCollection lists = site.Lists;
SPListTemplateType listTemplateType = new SPListTemplateType();
listTemplateType = SPListTemplateType.GenericList;
string listDescription = "This list retains vals inputted for the Post Travel form";
Guid ListId = lists.Add(listTitle, listDescription, listTemplateType);
. . .
This worked when first creating, and on subsequent execustions of the app.
However, I made some radical refactorings to the list structure and deleted the old one, so that (I hoped) a new one with the new structure would be created. However, instead of getting a refactored list, I got this on the last line shown above:
Microsoft.SharePoint.SPException was unhandled by user code
Message=Updates are currently disallowed on GET requests. To allow updates on a GET, set the 'AllowUnsafeUpdates' property on SPWeb.
I was able to resolve this by adding the indicated code:
site.AllowUnsafeUpdates = true;
...but why is this necessary? Why is the creation of a list which should no longer exist (I deleted it from the Sharepoint "All Site Content" bazaar) problematic (potentially an 'unsafe update')?

In an effort to protect itself from things like XSS and session hijacking, most updates to a SharePoint site are tied to a form digest control embedded on the same page from which the user makes the change. If the information from that form digest control isn't present when an update is triggered, SharePoint assumes the worst. This is especially important in cases where server-side code might execute with elevated privileges.
As you discovered, this is easily avoidable by switching the SPWeb object's AllowUnsafeUpdates property to true immediately before making any change.

Related

Find a specific field on a site and the set it to “Required” in a EventReciever

I have created a EventReciever that will be activated when a site gets created. This site have a announcement webpart. When they click on "add announcement", newform.aspx pops up as a dialog and there is a field there that is called Expired I need to make it required, so the field must be used else it prevent the user of adding a announcement.
How can I find that specific field and set it to Required in my EventReciever?
<!-- FieldName="Expires"
FieldInternalName="Expires"
FieldType="SPFieldDateTime"
-->
This is how I have progressed so far:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
if (properties.Feature.Parent is SPWeb)
{
var web = properties.Feature.Parent as SPWeb;
var expiredDate = web.Fields.GetFieldByInternalName("Expires");
expiredDate.Required = true;
expiredDate.Update();
}
}
Any kind of tips or help is appreciated!
Your WebPart will probably query a specific list? In that case it's also an option to set the Expires too required on that list.
Your current code will only update the Expires site column in the site, it will not push these changes to the lists using this column. If you were to create a new list after executing your code you would have a list where Expires is required.
However by reading your code my guess is that you actually want to update the current list and all new lists containing the column. In that case you should modify the expiredDate.Update() to expiredDate.Update(true). This boolean is an indication to SharePoint that you want the changes pushed down to existing lists. After executing this update you will see that your existing lists also require users to fill out an Expires date.

Sharepoint: web part programmatically changing permissions, cannot get the changes to persist

I'm writing a visual web part in visual studio for sharepoint 2010. The whole point of this web part is to change permissions around at the click of a button. I am able to access the permissions and output them to the screen. I'm able to change the permissions in the objects that I have, and then show the changed permissions on the screen. My problem is, after everything is done, no actual permissions have been changed.
webpart.TargetLibrary is a text field, inputted elsewhere as the name of the library I wish to investigate.
SPListCollection docLibs = m_SharePointWeb.GetListsOfType(SPBaseType.DocumentLibrary);
SPDocumentLibrary targetLib = (SPDocumentLibrary)(docLibs[webPart.TargetLibrary]);
SPListItemCollection libFolders = targetLib.Folders;
SPListItem folderItem = libFolders[0];
SPRoleAssignmentCollection folderRoles = folderItem.RoleAssignments;
SPRoleAssignment roleAssign = folderRoles[0];
SPRoleDefinitionBindingCollection spRDBC = roleAssign.RoleDefinitionBindings;
SPRoleDefinition Contribute = spRDBC[0].ParentWeb.RoleDefinitions["Contribute"];
folderItem.RoleAssignments[0].RoleDefinitionBindings.Add(Contribute);
folderItem.Update();
This is a somewhat simplified version - the original had some for loops and various bits of other code doing other things. Regardless, by everything I've been able to piece together from looking online, this code should add "Contribute" privs to the first role on the list in the first folder. As I mentioned, it does so to the local objects, but has no permanent effect. The Update() call appears to do nothing, and I'm not certain it's meant to be there in this case. I tried UpdateOverwriteVersion() - that doesn't do anything either. Any suggestions on what I might be doing wrong would be appreciated.
As a side note, it's not nearly so much of an issue, but I can't help the feeling that there's a more efficient and straightforward way to get a web object so that I can acquire role definitions by name. Any advice on that matter would also be appreciated.
Edit: solution moved to answer, below.
I have found answers to both the base and the side note.
For the side note, the following looks like it's the intended way to get an appropriate SPWeb object.
SPContext.Current.Web.RoleDefinitions["Contribute"];
For the base, the problem is that Sharepoint apparently does not save changing roles inside a SPRoleAssignment - only granting or removing permissions to the folder as a whole. This may or may not have to do with the fact that I was working inside a folder. In any case, in order to get it to save, you have to remove the person from SPRoleAssignmentCollection, make changes to their SPRoleAssignment, and then re-add the changed version. The correct version of the above code (integrating both answers) is as below.
SPRoleDefinition Contribute = SPContext.Current.Web.RoleDefinitions["Contribute"];
SPListCollection docLibs = m_SharePointWeb.GetListsOfType(SPBaseType.DocumentLibrary);
SPDocumentLibrary targetLib = (SPDocumentLibrary)(docLibs[webPart.TargetLibrary]);
SPListItemCollection libFolders = targetLib.Folders;
SPListItem folderItem = libFolders[0];
SPRoleAssignmentCollection folderRoles = folderItem.RoleAssignments;
SPRoleAssignment roleAssign = folderRoles[0];
folderRoles.Remove(roleAssign.Member);
roleAssign.RoleDefinitionBindings.Add(Contribute);
folderRoles.Add(roleAssign);

AccessDenied.aspx / List does not exist on list that user has access to

I am getting this error:
List does not exist
The page you selected contains a list that does not exist. It may have been deleted by another user. at Microsoft.SharePoint.Library.SPRequestInternalClass.GetListsWithCallback...
Unfortunately it's not the unpublished masterpage manifestation of the error - the list GUID in the query string leads to one of several lists in the root web that are used to display user settings in controls on the master page.
The list is being accessed using the following code:
_RootWeb = SPContext.Current.Site.RootWeb;
storageList = _RootWeb.GetList(_RootWeb.Url + "/Lists/" +
LocalStrings.TimeZoneStorageList.ListName);
SPListItemCollection result = list.GetItems(query);
SPListItem StorageItem = result[0];
xmlDoc.LoadXml(StorageItem[LocalStrings.TimeZoneStorageList.Fields.UserXML]
.ToString());
Is there anything that could cause that code to throw an AccessDeniedException while the user is able to access the list through the UI?
Alternatively, is it possible for SharePoint to report a list as the source of the error when the permissions issue is actually somewhere else entirely?
Is there anything that could cause
that code to throw an
AccessDeniedException while the user
is able to access the list through the
UI?
This might not apply in your case, but yes, I have had this happen with:
Lookup fields where the user did not have permissions to the lookup list
People fields filtered to a chosen SharePoint Group where the user did not have permissions to view the membership of the group.
In both cases, these fields were not included in the views or forms normally used by the user so the problem was not immediately discovered.
Can you try getting your list as
_RootWeb.Lists[strListName]
where strListName is the Title of the list.
The List ID in the query string on AccessDenied.aspx can in fact be unrelated to the actual error.
The relevant code was something like
try {
throw new AccessDeniedException();
}
catch (Exception ex) {}
storageList = _RootWeb.GetList(ListUrl);
If CatchAccessDeniedException is not disabled, SharePoint will see the exception despite the catch and abort the thread. Since this is controlled on another thread, it's not synchronous and the code continues to run long enough that the thread aborts in the middle of accessing the list.
Both parts of the error message are technically correct - There was an AccessDeniedException, and the last operation was trying to open the list - but there is no actual connection between the two.
The actual problem turned out to be service account permissions on mysites, which naturally never appeared in any logs or error messages.
The issue for me was quite simple. I had a user who had permissions to the current site, which was fine. However, there was a list web part on the page I got the error that accessed items from a sub site in which the current user didn't have access.
In my case the error should have read "access denied for list".

In Sharepoint, how do I update a task directly from a link in an email?

I'm just starting to use sharepoint designer and realised there's a lot that can be done to extend the basic features in sharepoint. We have an email alert sent out when a new task is created (by the user) and I want to customise the email so that it also includes a link called 'Assign'. When clicked, I want this link to automatically update the task with the assigned to field for the person that clicked it.
So I think the way to do this would be to hard-code the assign to value in the url behind this link, but I have no idea if this is possible or if there is an easier/better way to do this.
Any advice would be appreciated as I'm a complete beginner.
thanks.
I will not cover "How to modify the contents of an eamil alert" here as that is a seperate question and there are a lot of articles that cover that already.
For the Assigned link :-
You would need to create a custom page (or web part on an existing page) as the destination of your Assign link - this would take the Task ID as a query string param and then update the assigned to with the current user.
You could make this flexible by also taking the ListID but you may want to think about how this could be abused and put appropriate measures in place.
EDIT - in response to comment.
This is top of my head, not checked in compiler. This would have to sit on the same server as SharePoint to work as its using the OM - if you want to use a different server (why would you though) then look in the web services.
private void updateAssignedTo(Guid listId, int itemID)
{
SPWeb web = SPContent.Current.Web();
SPList list = web.Lists[listId];
SPListItem item = list.GetItemById(itemID);
item["Assigned To"] = web.CurrentUser;
item.Update();
}
You're going to have to work out how to get this code into to page or web part (SharePoint Designer is not going to cut it I think, you need Visual Studio) but its a starting point.

Add elements to an an announcements list for a specific user

I have a customer request to create a number of announcements based on some data from another database. Most of it seems easy enough but the new elements should be created by the user (login) specified in the input data.
I was planning to add the announcements using the list web services but I sure would like to avoid using impersonation in order to get the create user right. Is there a way to assign the correct user as the creator without using impersonation?
This may not be the answer you are looking for, but impersonation is pretty easy if you have code running in the GAC on a SharePoint server. You don't need to know any password which many do not realize, so I'll continue assuming that this was the reason you did not want to do impersonation. Here's how to do it.
You can connect to SharePoint using the typical constructor you use for SPSite and find the appropriate SPUser object. Once you do that, you can get the UserToken property for that SPUser. Then you'll need to use the SPSite constructor again, but use the overload that provides the SPUserToken. Then anything you do in SharePoint will be done via impersonation. No need to run with elevated privileges.
OK, now that I've said it in words, I'll try to guess at the code. It should be something like:
// Just determine the user token for a particular user
SPUserToken userToken = null;
using (SPSite tempSite = new SPSite("http://sharepointurl"))
{
using (SPWeb tempWeb = tempSite.OpenWeb())
{
// I think this next line works, but I'm going from memory
// I believe the user needs to have already logged into the site at least once
SPUser user = tempWeb.AllUsers["username"];
userToken = user.UserToken;
}
}
// Now do whatever we want impersonating that user
using (SPSite site = new SPSite("http://sharepointurl", userToken))
{
using (SPWeb web = site.OpenWeb())
{
// Do whatever you want here
}
}
I don't think that there is a method to archive this.
But perhaps this workaround might help. I have to admit I never tested this, it's only an idea how you might solve your problem.
You could try this. Create the new announcement with an admin user or with RunWithElevatedPrivileges(). After that use the RunWithElevatedPrivileges() method again and set the "created by" field to the user who should be the actual creator of the announcement. By this way only the "edited by" field should show the "wrong" user.
I know this is not a very elegant solution but it might work. ;)
I just realized that that my requirement was actually to circumvent the audit trail in SharePoint so I sure hope that it can't be done :-)
I came up with another solution: I added a new user or group field to the announcement list, and copy the AD-user logon into this field. Any report or view that previously used the "created by" field should now use the new field.
How about the situation where a real user enters a new element in the announcement list then? That will not update the new field with the logged in user!
Well, the only solution I could come up with is to add a ListItem Add trigger on the list. When a new element is added I check whether the new field contains a value, is not then I update the new field with the ID of the logged in user. That way the new field should always contain a valid userID.
I know this is not an elegant solution, but for the time being it is the best I can think of.
As alluded to in the answer's code comments, if the user has not visited the site at least one time, then there is no user metadata available from which to derive a proper usertoken.
With SharePoint 2010, you can simulate a user visit, with the EnsureUser method available from the SPWeb class (this snippet creates the user and also tweaks their profile a bit):
SPUser alice = web.EnsureUser(#"MYDOMAIN\alice");
SPList userInfo = web.SiteUserInfoList; //metadata storage of user info
SPListItem item = userInfo.GetItemById(alice.ID);
item["About Me"] = "I am Alice from Mel's Diner";
item.Update();

Resources