Forgive me reader, it has been a while since my last post.
I have a project that involved deploying three InfoPath browser-enabled forms made available on a SharePoint 2007 site. The site is an extranet site, accessed over the Internet by users who use an AD account and password to login.
The infrastruture uses ForeFront Threat Management Gateway (the successor to ISA server). Now TMG is a bit of a God-send for developers because it handles SSL for you. TMG hold the Server Certificate and takes all inbound https requests and maps them to http requests on port 80. Very neat, because it means that I can focus on developing the SharePoint site and not worry about SSL. Not quite.
I ran into some issues with the InfoPath forms. They worked absolutely fine when called internally: reading data from a web service upon load and then submitting to a web service on Submit. When I called the same forms when accessing the site externally, then I got several problems.
1. I could not load the forms at all initially.
2. When the form did load, the company logo would not display. IE gave me a warning message about unsecure content on the page and I could not disable it even by allowing Mixed Content in IE options.
3. The third error came when I tried to submit. "The form cannot be submitted to the Web server either because your computer is offline or because the host server is currently unavailable".
4. When the form closed, I got an error whereas it should have redirected back to the calling page. Again it was working fine internally.
The first problem is due to the supposed complexity of the url used to call the form (its the passed parameters). This is TMG rejecting the url. It is solved by modifying the HTTP policy on the Firewall rule to uncheck the options "Blocking high bit characters " and "Verify Normalization".
The second problem is fixed by the same solution as the third problem. If you take a look at the source code there is a variable g_objCurrentFormData that holds the content for the form. Buried in there you will find the url for the location of the xsn and its "Source" (the url it will return to) but InfoPath replaces the http:// with http:\u002f\u002f. Isn't that obscure? The issue is that it is using http and not https so you need to create a Link Translation on the Firewall policy that maps http:\u002f\u002fcontoso.com with https:\u002f\u002fcontoso.com. When that is done it, the form submitted successfully and also displayed the company logo without any warning messages from IE.
The fourth problem is due to the url for calling the form. It includes an &Source= parameter that is the return url for the form. But it uses the http url and that won't work externally. Trying to change it to be https also gave an error. So in the end I simply removed it, and the form still managed to return to the originating url. By the way, to force the form in the browser append &OpenIn=Browser.
So in the end I didn't have to make any changes to my form to make it work externally, it was all down to settings in TMG.
Go in peace - and say twenty Hail Mary's.
Wednesday, October 6, 2010
Sunday, July 18, 2010
Writing to MSMQ
I was recently trying to write a message to MSMQ on a remote server. I knew there were two steps but I didn't realize there was a third.
1. Modify the Queue name to the correct format
2. On the MSMQ queue give write access to the account you are running under
3. Install MSMQ on the source server
MSMQ name
The MSMQ format name is a particular structure you need to get right - including the case:
FormatName:DIRECT=OS:<servername>\private$\<queuename>
Write Access
If your code was running as a web service as mine was, then the App Pool identity must be a domain account and that account needs to have write access to the MSMQ on the target server.
Install MSMQ locally
I hadn't relaized this. You must install MSMQ in the local server where the code is running. The reason is that it logs messages in an Outbound Queue. This is what you would need if you were running in an off-line situation and then wanted the messages forwarded when you were on-line. I did not relaize that it would also do this even with a permanently connected enviroment. So install MSMQ locally and life will be good.
1. Modify the Queue name to the correct format
2. On the MSMQ queue give write access to the account you are running under
3. Install MSMQ on the source server
MSMQ name
The MSMQ format name is a particular structure you need to get right - including the case:
FormatName:DIRECT=OS:<servername>\private$\<queuename>
Write Access
If your code was running as a web service as mine was, then the App Pool identity must be a domain account and that account needs to have write access to the MSMQ on the target server.
Install MSMQ locally
I hadn't relaized this. You must install MSMQ in the local server where the code is running. The reason is that it logs messages in an Outbound Queue. This is what you would need if you were running in an off-line situation and then wanted the messages forwarded when you were on-line. I did not relaize that it would also do this even with a permanently connected enviroment. So install MSMQ locally and life will be good.
InfoPath 2007 and Forms Services - Deployment
I've always hated deploying InfpPath forms to a Production environment because I couldn't figure out how to change the path of any data connections without modifying the data connections with the appropriate url which means installing InfoPath in a Production environment.
I recently found that with an InfoPath form with managed code you can modify the manifest.xsf manually in NotePad and modify any urls. Publishing the form from VS will then create an XSN with the correct urls. This avoids using the data connection wizrd which will try to access the url.
Now I already new that you can convert the data connections into a UDCX file and then store that in a local data connections library but the path to the data connections library was still hard coded in the XSN with the server name. So if you deploy to production the InfoPath form is looking for the data connection library you used in development. It seemed like a ridiculous situation.
But finally I stumbled on this post which explains what to do. The post deploys the InfoPath form as a feature and uses WSP builder to bundle it as a solution.
The trick is to select "centrally managed data connection library (advanced)" when you modify the data connections. Copy the UDCX out of the data connection library that you used, modify it as desribed in the post. Then upload it in Central Administration -> Application Management -> Manage data connection files. Then deploy your InfoPath forms. Note that if you have managed code, the DLL not only has to be in the GAC but it also needs to be in the Feature folder .
I have to confess that we had major problems trying to create a feature for the InfoPath forms. It kept saying the form was not browser enabled which it clearly was. In the end because we only had 3 forms we gave up and installed it manually. Low and behold it worked in the browser perfectly. I should add that the forms are really quick to load, so much better than using the InfoPath client.
I recently found that with an InfoPath form with managed code you can modify the manifest.xsf manually in NotePad and modify any urls. Publishing the form from VS will then create an XSN with the correct urls. This avoids using the data connection wizrd which will try to access the url.
Now I already new that you can convert the data connections into a UDCX file and then store that in a local data connections library but the path to the data connections library was still hard coded in the XSN with the server name. So if you deploy to production the InfoPath form is looking for the data connection library you used in development. It seemed like a ridiculous situation.
But finally I stumbled on this post which explains what to do. The post deploys the InfoPath form as a feature and uses WSP builder to bundle it as a solution.
The trick is to select "centrally managed data connection library (advanced)" when you modify the data connections. Copy the UDCX out of the data connection library that you used, modify it as desribed in the post. Then upload it in Central Administration -> Application Management -> Manage data connection files. Then deploy your InfoPath forms. Note that if you have managed code, the DLL not only has to be in the GAC but it also needs to be in the Feature folder .
I have to confess that we had major problems trying to create a feature for the InfoPath forms. It kept saying the form was not browser enabled which it clearly was. In the end because we only had 3 forms we gave up and installed it manually. Low and behold it worked in the browser perfectly. I should add that the forms are really quick to load, so much better than using the InfoPath client.
Labels:
data connections,
deployment,
InfoPath 2007,
managed code,
UDCX
Thursday, May 13, 2010
InfoPath 2007 and Forms Services
I struggle to remember how to deploy an InfoPath 2007 form to SharePoint when it is a browser version. So here's the deal.
Create the InfoPath form and make sure the compatibility is set for browser version and that you make the form Fully Trusted and sign it with a digital certificate.
Now publish to form to a network location - its a temporary location and you can make it a local directory if you want. But when prompted to provide an alternate name for the form, leave this blank.
Create the InfoPath form and make sure the compatibility is set for browser version and that you make the form Fully Trusted and sign it with a digital certificate.
Now publish to form to a network location - its a temporary location and you can make it a local directory if you want. But when prompted to provide an alternate name for the form, leave this blank.
Now go to Central Administration and Application Centre and then select Upload Form template under InfoPath Form Services.
On the screen click Browse and navigate to where you uploaded the InfoPath template to. Click Verify and check for errors, if OK, then use Upload to upload the form template.
Go to Manage Form Templates and wait for the form to be installed and then right click and activate it to a site collection. What happens is the form is copied to the Form Template document library of the site collection (use View Site Content on the site collection in question to see the Form Templates library). It also adds the for as a site content type.
So now go to the top site of the site collection, choose Site Actions and Site Content Types. Scroll down the screen until you see the Content Type under Form Content types.
If you go to a Form Library and set it for supporting content types, you will be able to add en existing content type and then add the new form template.
If you go back to the Form Library and select New you should see the new Form type under the list. That will open the form template in the browser window.
So how is this different when the form has managed "code behind"? Answer: it's the same. In the case of an InfoPath form with managed code, you would create a Visual Studio project as an InfoPath form template and use your XSN as the source form. You will need to have installed the Office 2007 Visual Studio templates. Click on the Manifest.XSF to Open the form. The FormCode.cs has the code behind. From the Project menu you will be able to Build and then Publish the form. The same wizard is displayed and you would publish to a local directory leaving the laternate name of the form blank. The DLL that is created with the form is embedded in the XSN. Upload the form template in the same way.
On the screen click Browse and navigate to where you uploaded the InfoPath template to. Click Verify and check for errors, if OK, then use Upload to upload the form template.
Go to Manage Form Templates and wait for the form to be installed and then right click and activate it to a site collection. What happens is the form is copied to the Form Template document library of the site collection (use View Site Content on the site collection in question to see the Form Templates library). It also adds the for as a site content type.
So now go to the top site of the site collection, choose Site Actions and Site Content Types. Scroll down the screen until you see the Content Type under Form Content types.
If you go to a Form Library and set it for supporting content types, you will be able to add en existing content type and then add the new form template.
If you go back to the Form Library and select New you should see the new Form type under the list. That will open the form template in the browser window.
So how is this different when the form has managed "code behind"? Answer: it's the same. In the case of an InfoPath form with managed code, you would create a Visual Studio project as an InfoPath form template and use your XSN as the source form. You will need to have installed the Office 2007 Visual Studio templates. Click on the Manifest.XSF to Open the form. The FormCode.cs has the code behind. From the Project menu you will be able to Build and then Publish the form. The same wizard is displayed and you would publish to a local directory leaving the laternate name of the form blank. The DLL that is created with the form is embedded in the XSN. Upload the form template in the same way.
Tuesday, May 11, 2010
Converting XML to PDF
I was looking for an application that would convert an XML file into a PDF document. In fact I was trying to create an invoice as a PDF file were the invoice had originated from Great Plains (an application from the Microsoft Dynamics suite that claims to be an accounts package).
A colleague recommended Aspose.Words for .NET to me as he was using it for another project. It uses a Word document as a template and sets it up as a mailmerge. Now this made me a bit wary. I've had first hand expereince of programming a Word mailmerge and it wasn't a pleasant outcome. So the thought of doing a mailmerge in Word wasn't appealing. But it turns out that Aspose.Words for .Net has a single line API call that does the mailmerge for you. Brilliant!
doc.MailMerge.ExecuteWithRegions(GetTestOrderTotals());
But the second problem is a failing in Word 2007 - it doesn't support mailmerge with XML documents as a datasource. So I started looking at the examples that ship with Aspose.Words and stumbled on a Sales Invoice demo. In this case the Word document was using the NorthWinds Access database as the mailmerge source. In fact it was using 3 different views for the order header, the order line items and the order totals.
So how was I going to make that work with my XML source? A few minutes Googling gave me the answer. Word is simply using DataTables as the datasource so all I had to do was create a new DataTable with the same name as the one the Word document expected and create the appropriate columns with the correct data types. Then create a new row in the datatable and pass in the values extracted from the XML document. Bingo.
string Total = GetFieldFromXml(xmldoc, "eConnect/SOPTransactionType/taSopHdrIvcInsert/DOCAMNT");
// other strings declared and assigned here
ds.Tables.Add("OrderTotals");
ds.Tables[0].Columns.Add("OrderID");
ds.Tables[0].Columns.Add("Subtotal");
ds.Tables[0].Columns.Add("Freight");
ds.Tables[0].Columns.Add("Total");
ds.Tables[0].Rows.Add(OrderID, Subtotal, Freight, Total);
The order line items was easy to solve - I just created a DataRow for each node within my list of line items in the XML message.
What about creating the PDF document? Easy, just one line of code in Aspose.Words to specify that a PDF version is required and to open in the Web browser. I just did the usual trick of specifying the Response.ContentHeader to be application/pdf.
HttpResponse resp = Context.Response;
System.Text.Encoding enc = System.Text.Encoding.GetEncoding("ISO-8859-1");
resp.ContentEncoding = enc;
resp.ContentType = "application/pdf";
// now display PDF in browser
doc.Save("Aspose.Words.Demos.pdf", SaveFormat.Pdf, SaveType.OpenInBrowser, resp);
I was amazed how simple it was to create a PDF invoice. The great benefit of Aspose.Words is the use of the Word document as a template. It's a snip to modify the layout to get what you want. If you want to add extra fields you'll need to modify the views in Access to produce extra columns. No problem for me because I'm an old hand at Access but if you are unfamiliar with it then you might want to reconsider this option. But this is a great solution for my needs. Watch out though there is license cost to pay!
A colleague recommended Aspose.Words for .NET to me as he was using it for another project. It uses a Word document as a template and sets it up as a mailmerge. Now this made me a bit wary. I've had first hand expereince of programming a Word mailmerge and it wasn't a pleasant outcome. So the thought of doing a mailmerge in Word wasn't appealing. But it turns out that Aspose.Words for .Net has a single line API call that does the mailmerge for you. Brilliant!
doc.MailMerge.ExecuteWithRegions(GetTestOrderTotals());
But the second problem is a failing in Word 2007 - it doesn't support mailmerge with XML documents as a datasource. So I started looking at the examples that ship with Aspose.Words and stumbled on a Sales Invoice demo. In this case the Word document was using the NorthWinds Access database as the mailmerge source. In fact it was using 3 different views for the order header, the order line items and the order totals.
So how was I going to make that work with my XML source? A few minutes Googling gave me the answer. Word is simply using DataTables as the datasource so all I had to do was create a new DataTable with the same name as the one the Word document expected and create the appropriate columns with the correct data types. Then create a new row in the datatable and pass in the values extracted from the XML document. Bingo.
string Total = GetFieldFromXml(xmldoc, "eConnect/SOPTransactionType/taSopHdrIvcInsert/DOCAMNT");
// other strings declared and assigned here
ds.Tables.Add("OrderTotals");
ds.Tables[0].Columns.Add("OrderID");
ds.Tables[0].Columns.Add("Subtotal");
ds.Tables[0].Columns.Add("Freight");
ds.Tables[0].Columns.Add("Total");
ds.Tables[0].Rows.Add(OrderID, Subtotal, Freight, Total);
The order line items was easy to solve - I just created a DataRow for each node within my list of line items in the XML message.
What about creating the PDF document? Easy, just one line of code in Aspose.Words to specify that a PDF version is required and to open in the Web browser. I just did the usual trick of specifying the Response.ContentHeader to be application/pdf.
HttpResponse resp = Context.Response;
System.Text.Encoding enc = System.Text.Encoding.GetEncoding("ISO-8859-1");
resp.ContentEncoding = enc;
resp.ContentType = "application/pdf";
// now display PDF in browser
doc.Save("Aspose.Words.Demos.pdf", SaveFormat.Pdf, SaveType.OpenInBrowser, resp);
I was amazed how simple it was to create a PDF invoice. The great benefit of Aspose.Words is the use of the Word document as a template. It's a snip to modify the layout to get what you want. If you want to add extra fields you'll need to modify the views in Access to produce extra columns. No problem for me because I'm an old hand at Access but if you are unfamiliar with it then you might want to reconsider this option. But this is a great solution for my needs. Watch out though there is license cost to pay!
Thursday, January 21, 2010
Telnet commands to Send Email
I can never remember the commands to send an email via Telnet so here they are again mostly for my benefit. Examples of <smtp server> are
localhost
smtp.domain.com
C:\windows>telnet <smtp server> 25
Which should return
Connected to <smtp server>
HELO <smtp server>
MAIL FROM: admin@domain.com
RCPT TO: chuck@disney.com
DATA
Subject: Subject line of email
This text goes in the body of the email
<CR>.<CR>
Which should return
250 2.0.0 ???????? Message accepted for delivery
or
250 2.0.0 ???????? Message queued
IMPORTANT: The MAIL FROM email address should be a valid email address. Not only must the domain.com be the same domain the SMTP server is in, but don't assume that a made up email name will work. I've wasted hours trying to figure out why an email was not sent just because I didn't use a valid "mail from" address.
Should you not be able to connect to the SMTP server using the telnet command it may be because you have a firewall on the server that is blocking all outbound traffic on port 25. Or you've get the wrong SMTP server name.
localhost
smtp.domain.com
C:\windows>telnet <smtp server> 25
Which should return
Connected to <smtp server>
HELO <smtp server>
MAIL FROM: admin@domain.com
RCPT TO: chuck@disney.com
DATA
Subject: Subject line of email
This text goes in the body of the email
<CR>.<CR>
Which should return
250 2.0.0 ???????? Message accepted for delivery
or
250 2.0.0 ???????? Message queued
IMPORTANT: The MAIL FROM email address should be a valid email address. Not only must the domain.com be the same domain the SMTP server is in, but don't assume that a made up email name will work. I've wasted hours trying to figure out why an email was not sent just because I didn't use a valid "mail from" address.
Should you not be able to connect to the SMTP server using the telnet command it may be because you have a firewall on the server that is blocking all outbound traffic on port 25. Or you've get the wrong SMTP server name.
Invalid handle error in Visual Source Safe
I was getting an "Invalid handle" error message the other day when trying to access Visual Source Safe and it drove me mad for a couple of days until I found a solution. So I thought I would blog it in case you run across the same problem.
I was at a customer site using my laptop which was not connected to the customer's domain. When I plug in the network cable I get issued an IP address from their DHCP server. I then fire up my Virtual Machine with my development environment on it including Visual Source Safe 2005. The virtual machine network connections are configured so that it too is issued an IP address from the customer's DHCP.
Using the logon credentials the customer has issued me, I can connect to servers and file shares on their domain.
Now the Visual Source Safe database was located on one of the customer's servers and I noticed that the network connection was unreliable. At times when I pinged the server I got "request timed out" or "host destination unreachable". The result was when I opened Visual Source Safe sometimes it would work just fine and other times it would give me the "Invalid handle" error. It got to a point where I was unable to connect to Source Safe for several days.
The solution as it turned out was quite simple. I mapped a drive letter to a network share on the VSS server. Note it's important to map a drive letter, just using "Run" to connect to the share was not enough. Now of course if you can't even ping the server then mapping the drive won't work either. But I found that once I was able to map the drive the "Invalid handle" error would go away - at least for the rest of the day. I still find that each day I need to disconnect the drive and then reconnect but it's a small price to pay to have access to VSS for the rest of the day.
So if you come across the same situation I hope this helps you to solve it.
I was at a customer site using my laptop which was not connected to the customer's domain. When I plug in the network cable I get issued an IP address from their DHCP server. I then fire up my Virtual Machine with my development environment on it including Visual Source Safe 2005. The virtual machine network connections are configured so that it too is issued an IP address from the customer's DHCP.
Using the logon credentials the customer has issued me, I can connect to servers and file shares on their domain.
Now the Visual Source Safe database was located on one of the customer's servers and I noticed that the network connection was unreliable. At times when I pinged the server I got "request timed out" or "host destination unreachable". The result was when I opened Visual Source Safe sometimes it would work just fine and other times it would give me the "Invalid handle" error. It got to a point where I was unable to connect to Source Safe for several days.
The solution as it turned out was quite simple. I mapped a drive letter to a network share on the VSS server. Note it's important to map a drive letter, just using "Run" to connect to the share was not enough. Now of course if you can't even ping the server then mapping the drive won't work either. But I found that once I was able to map the drive the "Invalid handle" error would go away - at least for the rest of the day. I still find that each day I need to disconnect the drive and then reconnect but it's a small price to pay to have access to VSS for the rest of the day.
So if you come across the same situation I hope this helps you to solve it.
Subscribe to:
Posts (Atom)