Showing posts with label Sharepoint. Show all posts
Showing posts with label Sharepoint. Show all posts

Wednesday, August 1, 2012

Access SPListItem Attachment in Sharepoint 2010

My scenario was simple. Our company uses a financial vendor which has a web service for retrieving our Stock information and republishing it on our Sharepoint 2010 Intranet site. I successfully built a Timer Job that runs every 5 minutes during market open and close (deleting items older than a few days when market is closed) and it adds an item to a Sharepoint list attaching an XML file containing the results of the web service call. Working with past Stock applications in other languages I have found having the original, archived XML results is important to debug issues with the data provider.

The Timer Job works great and saves the files perfectly. Now I needed a Visual Web Part the user could add to their pages which would consume this XML file for the most recent entry, parse the information necessary then show the Stock Price in a pretty fashion per our marketing company's design. Honestly, I did not expect it to be difficult as I have done this a lot outside of .NET and Sharepoint. I setup the code to connect to my list, query the most recent item so I could grab its attachments. All good. With plenty of examples on the Google-nets, I quickly discovered how to get the URL of the attachment and could pass this to an XmlDocument object via Load() which lead to this code:


However, my code would not work and I continually received 404 errors trying to load the document via URL. I could take the same URL I was generating and put it in a browser window and successfully get to the list, but the code did not see it. I looked through logs, used Fiddler and even tried using HttpWebRequest and the LoadXml command as well with nothing shedding light on why this was erroring. I could not find any information from other people having this problem and even posted on MS' forums with no replies (although I was impatient and did not give it long).

My Timer Job was able to access urls outside the network, but this took some tricks by our Network and Server guys due to proxy issues. As such, I assume that maybe there was some permission/proxy issue hiding the list from my code, so I dug for other ways to try and load up this attachment. I found some references to people doing the same type of thing in Sharepoint 2003. Although I hate using old code since you never know what problems may exist using it in newer versions of Sharepoint and .NET, I was racing against my vacation and did not want to leave this hanging for weeks before I got back to it. So I replaced my code with the following which appears to be accessing the file in the list by its local folder structure and passing the binary stream to the XmlDocument Load command.


So far this is working and I have been able to use XPathNavigator to get the nodes I needed from the XML file. As is my MO, I posted this hoping to help anyone running into this issue and to get feedback from anyone that might have a better understanding of what is going on or a better way to accomplish this.

Monday, July 23, 2012

Sharepoint 2010 and List Picker Custom Web Part Property

One of the frustrations I have learning to code for Sharepoint is that the amount of information available is miserable. Either you get incomplete or unhelpful information in the way of sterile Microsoft Documentation, or any fool with a blog that has the potential to understand just as little as the reader about the subject matter. And YES I do - in fact - understand I fall into that category as well, but at least I try to be open about my ignorance and only try to post information which was helpful to me and my process of learning. So if you are learning to code from me, I sincerely apologize and hope you have some other resources at your disposal.

In any case, even when you find some one knowledgeable you also run into the fact that they may not specify whether they are talking about Sharepoint 2003, 2007 or 2010 (I ONLY deal with Sharepoint 2010 on my blog) or maybe leave off information they deem too complex or too simple. As was the case when I needed to have a custom webpart property that allowed a users to search for a List. I found DOZENS of ways to accomplish this that ranged from super simple (and useless) to absurdly complex. I tried many of these and most did not work properly or how I would expect. MS has several webparts that to do this (i.e. Content Query Web Part), so why is doing it yourself such a mystery?

As some background, for my project I needed a web part that would take a list, read in the rows and items and display this in a branding friendly manner for our publishing sites. As such, I wanted Content Owners to be able to add the web part and select a list they created somewhere in the site for it to use. I called this web part "QuickLinks" so keep that in mind as you read my code.

First off, you should understand a bit about adding custom properties to a web part. Here are a few links I found very helpful:
http://morshemesh.blogspot.com/2011/05/adding-custom-properties-to-visual-web.html
http://www.lamber.info/post/2010/05/21/How-do-I-create-custom-properties-in-Visual-Web-Parts.aspx
http://naimishpandya.wordpress.com/2012/01/12/how-to-add-custom-web-part-properties-to-visual-web-part-in-sharepoint-2010/

Once you have a handle on adding a custom property then a List Picker will not contain many surprises. The main difference is that you have to (for lack of a better term) build your picker before it can be added as a functional control. Even though you are calling a built in tool that MS uses and has available, you have to set the stage and provide some code before you add it. This seems like a little overkill to me, but I guess MS likes to make us developers sweat a little.

Here is my code for the ListPicker. As a note, I pieced this together from a variety of sources. Many of which served other purposes, so please forgive me if there is something weird in there.


Now you have your ListPicker ready for use, so we will use the other tricks to add this as a custom property. Next make sure your webpart.cs file defines the ToolboxItem as IWebEditable. I have included my entire QuickLinks.cs file below for an example.


Finally you will want to add the few lines to your main webpart code behind to so you can use the custom property. I have a stub of code below to demonstrate how I added it:


If this works for you like it worked for me, you just spent less time adding the List Picker than searching for how to do it. One thing that bothers me is I have seen this code for 2007 and it is not much different, so hopefully this is a supported way of doing this. However, if it works maybe its worth the risk.

Happy Sharepointing!

Friday, October 28, 2011

Issue with Site Collection Admins, AD groups and GetUserEffectivePermissions in Sharepoint 2010

As you may know I have been working on a custom Request Access page for Sharepoint 2010. During the process of this I ran into an interesting issue related to using AD Groups for your Site Collection Administrators and what is returned by GetUserEffectivePermissions and DoesUserHavePermissions. To simulate you can do the following:
  1. Create a New Site or Site Collection.
  2. Create an AD Group that contains "Admin, User1" and "Admin, User2".
  3.  Add this AD Group under Site Actions > Site Permissions and then Site Collection Administrators.
  4. Add "Owner, User1" and "Owner, User2" to your owners group, and "Member, User1" and "Member User2" in your members group.
Now create a small web part of aspx page that loops through and lists people with FullMask. I use FullMask because we are migrating sites from 2007 which mucks the owners group and we cannot rely on end users to actually use the owners group. The code I have used is:

using (SPSite siteCollection = new SPSite(SPContext.Current.Site.ID))
{
     int userCnt = 0;
     SPUserCollection userCollection = siteCollection.RootWeb.SiteUsers;
     foreach (SPUser user in userCollection)
     {
          string userSTR = user.Name;
          string userPERM = siteCollection.RootWeb.GetUserEffectivePermissions(user.LoginName).ToString();
          if (userPERM.Equals("FullMask") && !user.IsSiteAdmin)
          {
               userCnt++;
               ownersOUTPUT.Text += userSTR + "</br>";
          }
     }
}
When you run this you should only see "Owner, User1" and "Owner, User2", which would be what you would expect. Now add "Admin, User1" and "Members, User1" to your owners group and reload. As expected, you should now see "Admin, User1" and "Member, User1" listed as well. Finally, remove "Admin, User1" and "Member, User1" from your owners group and refresh. You should see what I am talking about.

"Member, User1" should disappear like you would expect, but "Admin, User1" is still listed and will not go away. Notice my code excludes users where IsSiteAdmin is true. Try adding "Admin, User1" directly to Site Collection Administrators and reload. Poof, he is gone.

This is because when a user is added directly to Site Collection Administrators IsSiteAdmin returns true; however, when the user has access via an AD Group in the same location IsSiteAdmin returns false. I assume that this works properly before "Admin, User1" is added directly because the users permissions do not exist via the sharepoint table. However, when you add the user to the site a record is create and is not properly cleaned up when removed due to them being referenced as Site Collection Administrator via the AD Group.

I have not been able to test removing a user from the AD Group as that is just not a simple job in my environment. I have contacted our Microsoft Rep for more information on this, and will provide more information once I recieve it.

Wednesday, October 26, 2011

Custom Request Access page in Sharepoint 2010 - The Full Story

To share a quote from the most interesting man in the world... I don't code for Sharepoint often, but when I do, I prefer to know what the hell I am doing.

Update (06/06/2012): Before today the code that was here ran the possibility of generating a NullException error getting the current user. I have updated the code to resolve this based on information from Microsoft. For a descriptive addendum, see the update at the end of this post.

Recently I was tasked with customizing the Request Access page in Sharepoint 2010. Our group is small and our users not so saavy, so we simply wanted to list all the Site Owners on the Request Access page. This way end users would know who they needed to contact if they did not have access to a site. Slam dunk, right? Well, so I thought.

First off, I am by no means an expert Sharepoint or .NET developer. At the time of this writing, I have only developed a handful of Sharepoint applications and have under 6 months of experience. I have been a developer for a long time, but Sharepoint and .NET are not my native tongues. As such, I will explain as much as I understand and did to complete my tasks, but the why's of all of this will only be assumptions. Please feel free to educate me if I speak in error or you feel the need to clarify any of this.

I started out with a Google search, as all quests for knowledge usually do, and found a lot of links on how to do this. One of the most concise posts was by Anmol Rehan:

http://www.anmolrehan-sharepointconsultant.com/2011/08/how-to-use-custom-access-denied-page-in.html

One problem that is not well explained is that this creates a Site (Site Collection) scoped feature which will set the custom page. UpdateMappedPage() is not available to Sandboxed Solutions and must be a Farm solution, and sets the custom page at the WebApplication level. However, due to recent security fixes, you should get an error when trying to deploy or activate your solution.This is because webApp.Update() has to reach up into the WebApplication to work properly. As such, make sure you have this scoped at the WebApplication level and NOT the Site Collection level; Otherwise activating/deploying will give you an Access Denied error in your logs.

Another problem, my main problem and an issue with all the other information I found, is it only tell you how to configure Sharepoint to load a custom page. Anmol lists a simple 3 step process; however, step 2 was a lot more involved - at least for me - since this does not detail exactly on what you have to do to create these pages. I posted this question on Microsoft's forums (my post), but - as seems to be usual - I owned my own thread and had to figure it out myself. As my post started to be the number 1 hit when searching for my problem, I figured I should document this for others that might run into the same issue. This seems fairly common, but maybe I am just so green I am the only one that needed help. Either way, I hope this helps someone.

So look at Anmol's post and get started, when you get to step 2 "Create your costom Access Denied Page in Layouts" this is what you do.
  1. Right-click on your project in the Solution Explorer and pull out the menu for Add then click Sharepoint "Layouts" Mapped Folder.
  2. Right-click on the folder automatically created under the Layouts folder in your project then pull out the Add menu, click New Item and then select Application Page.
  3. Copy the contents original page into your custom one (these are located in the hive under TEMPLATE/LAYOUTS/) and go crazy. Easy right?
Well, not really. What I found is that if I just built my page then I would either get a 403 Forbidden error or got redirected to the Access Denied page. These types of pages are considers Safeguarded Application Pages, and appear to have some restrictions. First off, there can be issues with dynamic master pages and - I assume - other things that keep you from just laying into developing your custom page.

My first issue was I was really used to working with the code-behind; however, this only seemed to work if the Page directive Inherits itself. This also seemed to be the core reason for the 403 and Access Denied redirection. Once I changed this to inhert the original page - in my case Microsoft.SharePoint.ApplicationPages.RequestAccess - I could actually start seeing my changes. Of course, I could not use code-behind as any references to the page went all red and squiggly.

So you need to put all your functionality into the aspx page. Microsoft supported using code behind, but for these pages it seemed troublesome. This is a small bit of information, but this could have saved me a few days of work. I was never able to find any rules to what you could and could not do with these pages, and I would still love to know the hard facts. However, in the end I was able to get things working. We are still working around some issues related to using AD groups as Site Collection Administrators, which tends to list the AD group members as FullMask with the IsSiteAdmin equal to false, but I am hoping this is just an issue with our development environment.

To close up, this is the code I ended up using in my custom ReqAcc.aspx page:


Thanks to everyone for your support and help!

Update: For those that used the old version of the code (prior to 06/06/2012), here is the itemized changes:
  1. Replace SPUser reqUser = SPContext.Current.Web.CurrentUser; with string strUser = String.Format("{0}\\{1}", Environment.UserDomainName, Environment.UserName); and import the System namespace (<%@ Import Namespace="System" %>) You then need to convert this to a user object once you get into your RunWithElevatedPrivileges block with SPUser reqUser = thisroot.RootWeb.SiteUsers[strUser];. This is because the CurrentUser context was removed in these Safeguard Application pages as there was a bug allowing users to elevate privileges.
  2. I also now scope this as a WebApplication solution as restrictions were also put in place to disallow a Site Collection from updating the WebApplication. This makes sense, but limits us actually controlling this at a Site Collection.
  3. Added additional code I wrote for emailing site owners for access.