Delphi Technology Solutions - development, websites, computers and networks

Development   |   Computers & Networks   |   Websites   |   Products & Services   |   Client Success   |   News

David's Ramblings on Development

SharePoint, InfoPath, .Net and more




Accessing User's Sharepoint Permissions in InfoPath

Share |
clock November 4, 2009 12:16 by author David Lozzi

In one of my customer's MOSS implementations we're using SharePoint's resident permission management to control who has access to what InfoPath forms. The issue we came across is that if a user has read-only access to a form and opens it, they can see the Update button. Clicking it does error and denies the change, and after the error the user gets the typical form closed message.

We wanted to hide the button from the view of a user who has read-only permissions to deny the error all together and minimize user complaints.

Using the code behind in the InfoPath form, we can check the current user against the current item then set a field in InfoPath to flag whether or not the user is read-only or not. From there, we can base the conditional formatting of buttons, text boxes, etc. on this value.

try
{
 if (e.InputParameters.ContainsKey("XmlLocation") && !SPContext.Current.Web.CurrentUser.IsSiteAdmin)  //Regardless of resident permissions, give SiteAdmins access
 {
  SPUser curUser = SPContext.Current.Web.CurrentUser;
  string path = e.InputParameters["XmlLocation"].ToString(); //get the location of the XML file and parse
  string lib = path.Substring(0, path.LastIndexOf("/"));
  string formName = path.Substring(path.LastIndexOf("/") + 1);
  using (SPSite site = new SPSite(SPContext.Current.Site.ID))
  {
   SPWeb web = site.OpenWeb(SPContext.Current.Web.ID); //grab the current web
   SPListItem li = web.GetListItem(path.Replace(web.Url, "")); //grab the current list item using the

   if (li.DoesUserHavePermissions(SPBasePermissions.EditListItems)) //check for permissions
    SetNodeValue("/my:mySAR/my:IsReadOnly", "0"); //if it exists, set field to false
   else
    SetNodeValue("/my:mySAR/my:IsReadOnly", "1"); //if it doesn't, set it to true
  }

 }
 else
  SetNodeValue("/my:mySAR/my:IsReadOnly", "0"); //if the XmlLocation doesn't exist, it's a new form so it's not Read Only
}
catch (Exception ex)
{
 WriteErrorToLog("FormEvents_Loading", "Problem setting user permissions", ex);
 SetNodeValue("/my:mySAR/my:IsReadOnly", "1"); //on error stop the user from writing
}

From here I based the buttons' conditional formatting on the field IsReadOnly.





Using SharePoint properties to save values instead of web.config

Share |
clock July 6, 2009 09:31 by author David Lozzi

A while back I ran into this issue: I had two web parts in the same project but I wanted them to share settings. As my experience as an ASP.Net developer was pointing me to use the web.config file, instead I thought SharePoint must have something similar to this without me having to mess with the web.config file. After searching and posting on MSDN, I did finally find a solution that uses the properties of SharePoint Sites and Webs.

Below is a sample code that stores the Employee Profile Group in the web so two web parts and an InfoPath form can access the values.

The GetWebProperty function creates the property if it doesn't exist, and if it does it'll return the string value. It's important to call this function before setting a web property, especially the first time it's run so the property is created. I suppose you could technically put the same creation functionality in the SetWebProperty as well, but I was using Get (getting values into my webpart) before my Set (using edit pane to view it).

string employeeProfile = GetWebProperty("EmployeeProfileUserGroup");

SetWebProperty("EmployeeProfileUserGroup", pe.DisplayText);

static string GetWebProperty(string Name)
{
 using (SPSite site = new SPSite(SPContext.Current.Site.ID))
 {
  SPWeb web = site.OpenWeb(SPContext.Current.Web.ID);
  if (web.Properties[Name] == null)
  {
   web.AllowUnsafeUpdates = true;
   web.Properties.Add(Name, "");
   web.Properties.Update();
   web.AllowUnsafeUpdates = false;
   return "";
  }
  else
  {
   return web.Properties[Name].ToString();
  }
 }
}
void SetWebProperty(string Name, string Value)
{
 using (SPSite site = new SPSite(SPContext.Current.Site.ID))
 {
  SPWeb web = site.OpenWeb(SPContext.Current.Web.ID);
  GetWebProperty(Name); //ensure the property exists
  web.AllowUnsafeUpdates = true;
  web.Properties[Name] = Value;
  web.Properties.Update();
  web.AllowUnsafeUpdates = false;
 }
}

If you would like to share the same properties across multiple webs, use the Site.RootWeb object and store all properties there!

Enjoy!





Versioned Comments in InfoPath

Share |
clock July 2, 2009 16:57 by author David Lozzi

One great feature of SharePoint and versioning is the ability to have versioned comments. This can allow a small discussion to occur on almost any item. It also allows someone to keep a running log of notes on certain items.

I had the wonderful task of figuring out how to get this to work the same in InfoPath. Below is a function that will append the notes into a repeating group. On submit of the form, I call this function and it works great, everytime!

public void AddAnalystNotes()
{
 try
 { //this is the text box the user enters their notes into.
  if (GetNodeValue(GetRootNodeName() + "/my:DetailsSection/my:AddAnalystNotes/my:AddNotes") != "")
  { //this is the group in the datasource just above the repeating group
   XPathNavigator selReqs = MainDataSource.CreateNavigator().SelectSingleNode(
   GetRootNodeName() + "/my:DetailsSection/my:AnalystNotesGroup", NamespaceManager);
   
   string nsPrefix = selReqs.Prefix;
   string nsURI = selReqs.NamespaceURI;
   
   //this is the repeating group of notes
   XPathNodeIterator selAnalyst = MainDataSource.CreateNavigator().Select(
   GetRootNodeName() + "/my:DetailsSection/my:AnalystNotesGroup/my:AnalystNotes", NamespaceManager);
   
   XmlWriter writer;
   //if there are already items in the list, pop this new note at the top
   if(selAnalyst.Count > 0){
    selReqs.MoveToChild("AnalystNotes",nsURI);
    writer = selReqs.InsertBefore();
   }else{
    writer = selReqs.AppendChild();
   }
   
   //now write the values
   writer.WriteStartElement(nsPrefix, "AnalystNotes", nsURI);
   writer.WriteElementString(nsPrefix, "CreatedDate", nsURI, DateTime.Now.ToString());
   writer.WriteElementString(nsPrefix, "CreatedBy", nsURI, SPContext.Current.Web.CurrentUser.Name);
   writer.WriteElementString(nsPrefix, "Notes", nsURI, ""); //have to create a blank entry then add the value later down the line
   writer.WriteEndElement();
   writer.Close();
   
   //gets the text box with the new notes in it
   XPathNavigator xnotes = MainDataSource.CreateNavigator().SelectSingleNode(
   GetRootNodeName() + "/my:DetailsSection/my:AddAnalystNotes/my:AddNotes",NamespaceManager);
   
   selAnalyst.MoveNext(); //grab the first element, the default is root
   
   //had to use InnerXml, the value to value loses HTML formatting
   selAnalyst.Current.SelectSingleNode("my:Notes",NamespaceManager).InnerXml = xnotes.InnerXml;

   //clears the text box
   SetNodeValue(GetRootNodeName() + "/my:DetailsSection/my:AddAnalystNotes/my:AddNotes", "");
  }
 }
 catch (Exception ex)
 {
  WriteErrorToLog("addRequestor_Click", "Adding analyst noteserrored", ex);
 }
}

Then in the view, format the repeating group to be read-only and blamo, versioned comments.

 





Duplicate site columns in MOSS from InfoPath Forms

Share |
clock June 3, 2009 18:37 by author David Lozzi
It appears my customer and I are on the cutting edge of InfoPath development with Form Services. We are CONSTANTLY running into issues and bugs. I try my best to post what we find to help anyone else running through the same issues. This issue is a doosy.
 
Our scenario is this: We have 3 server farms setup, one for development, one for staging and testing and one for production. We are creating InfoPath forms in Visual Studio 2008 and each have loads of codebehind (handling loading, saving, webservices, etc). We publish the form, with several promoted fields, to Dev first and make sure it works, then publish it to Staging, then after rounds of testing it's released on the Production server. The promoted fields are almost assigned to existing site columns. This works great, in theory.
 
The issue is that once in a while, for no explained reason, whether publishing to any of the three servers, if I were to add a new field to a form, or modify the schema a little, that's when it hits the fan. The magic that is the InfoPath Publishing Wizard will decide to create a new site column for a promoted field that was already assigned to a site column! YES, it's true!
 
So for example we have a field in every InfoPath form called FX Form ID. Walking through the wizard, I make sure the FX Form ID field is bound to the correct site column. After the wizard is complete, I deactivate the current form, upload the form and the reactivate it. I take a look at Site Columns in Site Settings and I now see TWO FX Form ID FIELDS! Grrr...
 
I pulled up SharePoint Manager 2007 (awesome tool for analyzing exactly what is going on in SharePoint) on the server and took a look at the list of fields for the root web. Sure enough, there are two of the same field. They are exactly the same except for the Ids and Internal Names are different, each their own unique GUID.
 
Unfortunately, there is no easy way to remap the existing data and fields into a single column. I'd bet there is a way in the SQL tables to do it, but that'll void any support from Microsoft, and probably make matters worse. What you can do is moving forward, make sure it doesn't happen again.
 
The Work Around
 
Working closely with a Microsoft SharePoint Architect (as soon as he get's a blog I'll point you to it), we figured out a workaround. It works great, just very tedious. Here's what you do:
 
1. Close the mnifest.xsf design view and open it in XML editor (right click and select Open Wth)
 
2. Search for the field name, in this example Form ID. You should see something similiar to this towards the end of the file.
 
<xsf:field name="Form ID" columnName="{81BDA8CC-A286-405F-8A3B-E5ADB18D9FFB}" node="/my:myFields/fxformstate:FormState/fusionx:FormID" type="xsd:string"></xsf:field>

3. Now search for the columnName value, the GUID, in this same file. You should find something like this.

<xsf2:fieldExtension columnId="f765018d-6b66-4b03-8408-2d6a1f8f0060" readWrite="yes" columnName="{81BDA8CC-A286-405F-8A3B-E5ADB18D9FFB}"></xsf2:fieldExtension>

4. Note the columId value and open SharePoint Manager 2007 on the SharePoint server.
 
5. Expand the website then Fields (or if you want to see what existing forms are using, expand ContentTypes then the form name, then Fields).
 
6. Find the field and note the Id. Does it match the columnId value from the manifest file? If so, then you're fine, go get a coffee.
 
7. If they do not match, then InfoPath thought better of using the existing column and wanted to use another. Or if your columnId is blank, ="", then it wanted to create a new one. This here is the mystery, why did InfoPath decide the field you selected wasn't important enough, I don't know.
 
8. Copy the value from SPM and replace the value in the columnId in the manifest file.
 
9. Close the manifest file and open it normally in VS, allowing the design view to appear. You can now publish the form. When publishing, DO NOT modify the field in the Column list. This may overwrite the value you entered in. Continue through the publishing wizard and upload to MOSS.
 
If all goes well, the new content type or the upgrade should use the same column as selected. To verify, open SharePoint Manager on the server, expand the website then Content Types and find your form name and check the ID of the Field.
 
I hope this helps!




Programmatically Send Parameters to a Web Service in InfoPath

Share |
clock April 16, 2009 20:26 by author David Lozzi

I wrote a web service that queries the users in certain groups and I bound a dropdown in my InfoPath form to it. The web service requires two values, the site name and the group name. In using the InfoPath Data Connection wizard I specified the values.

We now are looking to move this form to another server (from dev to staging to prod) and this form is going to become the base for about a dozen other forms, so changing this value manually every time will become a hassel at best.

I figured out how to update the webservice from the code behind, and send it the values I want to send it. Very effective and useful.

IMPORTANT First thing though, you need to stop the web service data connection from getting values on start up. That option is in the last screen of the data connection wizard.

Then, in the Form_Load use the similar code as below and blamo, you're off and running.

WebServiceConnection webservConn = (WebServiceConnection)DataConnections["ListGroupUsers"];

XmlDocument inputDoc = new XmlDocument();

inputDoc.LoadXml("<ListGroupUsers xmlns=\"http://site/\"><SiteURL>" + SPContext.Current.Web.Url + "</SiteURL><GroupName>Site Members</GroupName></ListGroupUsers>");

XPathNavigator inputNav = inputDoc.CreateNavigator();

XmlDocument outputDoc = new XmlDocument();

XPathNavigator outputNav = outputDoc.CreateNavigator();

XmlDocument errorDoc = new XmlDocument();

XPathNavigator errorNav = errorDoc.CreateNavigator();

//Need to specify the XPath to dump the results into, so grab the Web Service repsonse

XPathNavigator root = MainDataSource.CreateNavigator();

XPathNavigator node = root.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:ListGroupUsersResponse/tns:ListGroupUsersResult", NamespaceManager);

webservConn.Execute(inputNav,node,errorNav);

Hope this helps!





Unused DataSources in InfoPath

Share |
clock August 25, 2008 17:34 by author David Lozzi
So this is quite the interesting issue. Apparently InfoPath HATES data sources that are not being used. If you have a data source that's not assigned to anything, say a dropdown list or something, then the form doesn't like life. For example, I had a data source that was in my IP form which was the lookup for a dropdown but then I replaced it with another lookup list. Now this data source is doing nothing, just hanging out, unassigned and alone. The data source gets moody, frustrated and then doesn't play nice with the other objects in the form.

The error I received is:

Type: XmlException, Exception Message: Unexpected end of file has occurred. The following elements are not closed: dfs:dataFields, dfs:myFields. Line 1, position 328.)

Yeah, that really helps! I ran into this one time before, where a rogue data source was causing issues, but I forget the error I was getting then. If I remember, I'll throw it up here. If you happened by this post and are receiving a different error, let me know so I can share with all!

I hope this helps!
 




RSSRSS Subscribe

About David Lozzi

I love what I do. I'm not the sketchy type that hides in his basement coding all day. I have a beautiful wife and two great children. I've spent my last 10 years plus in the technology arena. more...

Login