I have a bunch of InfoPath form templates (xsn) which I want to upload to a SharePoint list programmatically. My program has to upload these form templates to different lists based on predefined logic. When I upload the browser-enabled form templates (xsn) with my code, the forms do not work:
/// <summary>
/// Uploads a file to the specified sharepoint list
/// </summary>
/// <param name="listName"></param>
/// <param name="fileInfo"></param>
/// <param name="listVersion"></param>
/// <returns></returns>
public static bool UploadFile(string listName, FileInfo fileInfo, string listVersion)
{
WebRequest request = WebRequest.Create(fileInfo.URL);
request.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
request.Method = "PUT";
byte[] buffer = new byte[1024];
using (Stream stream = request.GetRequestStream())
{
using (MemoryStream ms = new MemoryStream(fileInfo.Bytes))
{
for (int i = ms.Read(buffer, 0, buffer.Length); i > 0; i = ms.Read(buffer, 0, buffer.Length))
stream.Write(buffer, 0, i);
}
}
WebResponse response = request.GetResponse();
response.Close();
var client = new Lists.ListsSoapClient();
var batch = new XElement("Batch",
new XAttribute("OnError", "Continue"),
new XAttribute("ListVersion", listVersion),
new XAttribute("PreCalc", "TRUE"));
var method = new XElement("Method",
new XAttribute("ID", "1"),
new XAttribute("Cmd", "Update"),
new XElement("Field",
new XAttribute("Name", "ID")),
new XElement("Field",
new XAttribute("Name", "FileRef"),
fileInfo.URL));
foreach (string key in fileInfo.Properties.Keys)
{
object value = fileInfo.Properties[key];
method.Add(new XElement("Field",
new XAttribute("Name", key),
fileInfo.Properties[key]));
}
batch.Add(method);
var element = client.UpdateListItems(listName, batch);
var code = element.Elements().First().Elements().First().Value;
if (code != "0x00000000")
throw new Exception(code);
return true;
}
It seems there is more to be done that just pushing a file stream into the list.
Anyone have an idea how to do this?
EDIT More specifically, the error message I get is: This form template is not enabled for viewing in the browser.
UPDATE When I publish the same form with Microsoft InfoPath it works.
you can use this code to convert your uploaded form in browser enabled form as:
FormsService localFormsService;
SPFarm localFarm = SPFarm.Local;
SPSite localSite = new SPSite("http://ServerName");
SPWeb localWeb = localSite.AllWebs["SiteName"];
try
{
localFormsService = localFarm.Services.GetValue<FormsService>(FormsService.ServiceName);
SPFile localFile = localWeb.GetFile("FormLibrary/Forms/FormTemplate.xsn");
localFormsService.BrowserEnableUserFormTemplate(localFile);
Console.Write("Press Enter to Continue");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
Console.Write("Press Enter to Continue");
Console.ReadLine();
}
or you can use this link for more details as:
http://msdn.microsoft.com/en-us/library/microsoft.office.infopath.server.administration.formsservice.browserenableuserformtemplate.aspx
Related
I'm trying to save a generic list and get it back by using a BinaryFormatter but I can't get the list in the form that I have saved, it returns me only the first item in the list. I think there might be an error while the code tries not to overwrite the file. If you need more details, please tell me and I'll add the details that you need.
#region Save
/// <summary>
/// Saves the given object to the given path as a data in a generic list.
/// </summary>
protected static void Save<T>(string path, object objectToSave)
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream;
if (!File.Exists(path))
{
stream = File.Create(path);
}
else
{
stream = File.Open(path, FileMode.Open);
}
List<T> list = new List<T>();
try
{
list = (List<T>)formatter.Deserialize(stream);
}
catch
{
}
list.Add((T)objectToSave);
formatter.Serialize(stream, list);
stream.Close();
}
#endregion
#region Load
/// <summary>
/// Loads the data from given path and returns a list of questions.
/// </summary>
protected static List<T> Load<T>(string path)
{
if (!File.Exists(path))
{
System.Windows.Forms.MessageBox.Show(path + " yolunda bir dosya bulunamadı!");
return null;
}
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = File.Open(path, FileMode.Open);
List<T> newList;
try
{
newList = (List<T>)formatter.Deserialize(stream);
}
catch
{
newList = null;
}
stream.Close();
return newList;
}
#endregion
Okey, I just figured the problem. Appearently if you make a change in the data without saving it (I did it in "list = (List)formatter.Deserialize(stream);" this line of code) and then if you try to serialize it again, the FileStrem that you are using doesn't work generically, so you have to close the old stream and than reopen it or another again or just simply type stream = File.Open(path, FileMode.Open); again. Thanks anyway :D
We are using AutoRest for generating client code based on API Swagger files.
I'm trying to pass client certificate to the API. But noticed that generated client code doesn't accept WebRequestHandler.
Generated code looks like below:
public MyTestApiV1(Uri baseUri, params DelegatingHandler[] handlers) : this(handlers)
{
if (baseUri == null)
{
throw new ArgumentNullException("baseUri");
}
this.BaseUri = baseUri;
}
I feel like I'm missing something here. Has anyone managed to send client certificate using AutoRest?
Tried this but webRequestHandler is always null:
var webRequestHandler = client.HttpMessageHandlers.First() as WebRequestHandler;
if (webRequestHandler != null)
{
var secretRetrieved = keyVault.GetSecretAsync("my-cert");
var pfxBytes = Convert.FromBase64String(secretRetrieved.Result);
// or recreate the certificate directly
var certificate = new X509Certificate2(pfxBytes);
webRequestHandler.ClientCertificates.Add(certificate);
}
You can use another overloaded constructor:
/// <summary>
/// Initializes ServiceClient using base HttpClientHandler and list of handlers.
/// </summary>
/// <param name="rootHandler">Base HttpClientHandler.</param>
/// <param name="handlers">List of handlers from top to bottom (outer handler is the first in the list)</param>
protected ServiceClient(HttpClientHandler rootHandler, params DelegatingHandler[] handlers)
ServiceClient is the base class for generated clients. Therefore, code might look like:
var secretRetrieved = keyVault.GetSecretAsync("my-cert");
var pfxBytes = Convert.FromBase64String(secretRetrieved.Result);
// or recreate the certificate directly
var certificate = new X509Certificate2(pfxBytes);
WebRequestHandler webRequestHandler = new WebRequestHandler();
webRequestHandler.ClientCertificates.Add(certificate);
var client = new MyTestApiV1(webRequestHandler);
client.BaseUri = baseUri;
.net Core version
Ivan R's answer led me on the right path but it's a little different for .net core (2.2 at this point in time) as WebRequestHandler is not available in core.
I had to use a pfx file and password in my case. GetNumberPassedIn isn't in the generic Petstore Swagger template but was what I was testing with.
Program.cs:
using System;
using System.Net.Http;
namespace SimpleApi2.Console
{
class Program
{
static void Main(string[] args)
{
var certificate = new CertInfo().GetCertFromPfx(Const.PfxPath, Const.PfxPassword);
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(certificate);
var client = new HttpClient(handler);
var petStore = new SwaggerPetstore(client, true);
petStore.BaseUri = new Uri(Const.PublicUrl);
var result = petStore.GetNumberPassedIn(135, Const.ApiKey);
System.Console.WriteLine(result.ToString());
System.Console.ReadKey();
}
}
}
CertInfo.cs:
using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Security;
namespace SimpleApi2.Console
{
class CertInfo
{
internal static byte[] ReadFile(string fileName)
{
FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read);
int size = (int)f.Length;
byte[] data = new byte[size];
f.Read(data, 0, size);
f.Close();
return data;
}
public CertInfo() { }
public X509Certificate2 GetCertFromPfx(string pfxFilePath, string password)
{
try
{
byte[] rawData = ReadFile(pfxFilePath);
var passwordAsChars = password.ToCharArray();
var securePassword = new SecureString();
foreach (char c in password)
securePassword.AppendChar(c);
securePassword.MakeReadOnly();
X509Certificate2 x509 = new X509Certificate2(pfxFilePath, password,
X509KeyStorageFlags.UserKeySet);
WriteCertInfo(x509);
return x509;
}
catch (DirectoryNotFoundException)
{
System.Console.WriteLine("Error: The directory specified could not be found.");
throw;
}
catch (IOException)
{
System.Console.WriteLine("Error: A file in the directory could not be accessed.");
throw;
}
catch (NullReferenceException)
{
System.Console.WriteLine("File must be a .cer file. Program does not have access to that type of file.");
throw;
}
}
private static void WriteCertInfo(X509Certificate2 x509)
{
//Print to console information contained in the certificate.
System.Console.WriteLine("{0}Subject: {1}{0}", Environment.NewLine, x509.Subject);
System.Console.WriteLine("{0}Issuer: {1}{0}", Environment.NewLine, x509.Issuer);
System.Console.WriteLine("{0}Version: {1}{0}", Environment.NewLine, x509.Version);
System.Console.WriteLine("{0}Valid Date: {1}{0}", Environment.NewLine, x509.NotBefore);
System.Console.WriteLine("{0}Expiry Date: {1}{0}", Environment.NewLine, x509.NotAfter);
System.Console.WriteLine("{0}Thumbprint: {1}{0}", Environment.NewLine, x509.Thumbprint);
System.Console.WriteLine("{0}Serial Number: {1}{0}", Environment.NewLine, x509.SerialNumber);
System.Console.WriteLine("{0}Friendly Name: {1}{0}", Environment.NewLine, x509.PublicKey.Oid.FriendlyName);
System.Console.WriteLine("{0}Public Key Format: {1}{0}", Environment.NewLine, x509.PublicKey.EncodedKeyValue.Format(true));
System.Console.WriteLine("{0}Raw Data Length: {1}{0}", Environment.NewLine, x509.RawData.Length);
System.Console.WriteLine("{0}Certificate to string: {1}{0}", Environment.NewLine, x509.ToString(true));
}
}
}
I want to add a functionality of adding a watermark using itextSharp library to the pdf document that is being added to the library. For this I created an event listener that is triggered when item is being added. The code is as follows :
using System;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.IO;
namespace ProjectPrac.WaterMarkOnUpload
{
/// <summary>
/// List Item Events
/// </summary>
public class WaterMarkOnUpload : SPItemEventReceiver
{
/// <summary>
/// An item is being added.
/// </summary>
public override void ItemAdding(SPItemEventProperties properties)
{
base.ItemAdding(properties);
string watermarkedFile = "Watermarked.pdf";
// Creating watermark on a separate layer
// Creating iTextSharp.text.pdf.PdfReader object to read the Existing PDF Document
PdfReader reader1 = new PdfReader("C:\\Users\\Desktop\\Hello.pdf"); //THE RELATIVE PATH
using (FileStream fs = new FileStream(watermarkedFile, FileMode.Create, FileAccess.Write, FileShare.None))
// Creating iTextSharp.text.pdf.PdfStamper object to write Data from iTextSharp.text.pdf.PdfReader object to FileStream object
using (PdfStamper stamper = new PdfStamper(reader1, fs))
{
// Getting total number of pages of the Existing Document
int pageCount = reader1.NumberOfPages;
// Create New Layer for Watermark
PdfLayer layer = new PdfLayer("WatermarkLayer", stamper.Writer);
// Loop through each Page
for (int i = 1; i <= pageCount; i++)
{
// Getting the Page Size
Rectangle rect = reader1.GetPageSize(i);
// Get the ContentByte object
PdfContentByte cb = stamper.GetUnderContent(i);
// Tell the cb that the next commands should be "bound" to this new layer
cb.BeginLayer(layer);
cb.SetFontAndSize(BaseFont.CreateFont(
BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED), 50);
PdfGState gState = new PdfGState();
gState.FillOpacity = 0.25f;
cb.SetGState(gState);
cb.SetColorFill(BaseColor.BLACK);
cb.BeginText();
cb.ShowTextAligned(PdfContentByte.ALIGN_CENTER, "Confidential", rect.Width / 2, rect.Height / 2, 45f);
cb.EndText();
// Close the layer
cb.EndLayer();
}
}
}
I want to know how to add the path without hardcoding it here :
PdfReader reader1 = new PdfReader("C:\\Users\\Desktop\\Hello.pdf"); //THE RELATIVE PATH
And then uploading the watermarked document to the library and not the original pdf.
I know that it can also be done through workflow but I am pretty new to sharepoint. So if at all you have an answer that has workflow in it please give the link that explains the workflow for automating the pdf watermarking.
You don't need to have workflow to achieve what you are looking for:
First, use ItemAdded event instead of ItemAdding. Then you can access SPFile associated with updated list item.
public override void ItemAdded(SPItemEventProperties properties)
{
var password = string.Empty; //or you put some password handling
SPListItem listItemToFile = properties.Listitem;
SPFile pdfOriginalFile = listItemToFile.File;
//get byte[] of uploaded file
byte[] contentPdfOriginalFile = pdfOriginalFile.OpenBinary();
//create reader from byte[]
var pdfReader = new PdfReader(new RandomAccessFileOrArray(contentPdfOriginalFile), password);
using (var ms = new MemoryStream()) {
using (var stamper = new PdfStamper(pdfReader, ms, '\0', true)) {
// do your watermarking stuff
...
// resuming SP stuff
}
var watermarkedPdfContent = ms.ToArray();
base.EventFiringEnabled = false; //to prevent other events being fired
var folder = pdfOriginalFile.ParentFolder;//you want to upload to the same place
folder.Files.Add(contentPdfOriginalFile.Name, fs.ToArray(),true);
base.EventFiringEnabled = true;
}
}
I probably did a typo or two since I didn't run this code. However, it should give you an idea.
I'm loading my document like so:
WebClient client = new WebClient();
client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
client.OpenReadAsync(new Uri("Rolls.xml", UriKind.Relative));
Then on the Read Completed:
XDocument doc = XDocument.Load(XmlReader.Create(e.Result));
using (Stream stream = e.Result)
{
{
foreach (var roll in _rollsToAddStudentTo)
{
doc.Element("rolls").Add(new XElement("rollid", roll));
}
doc.Save(stream);
}
}
The problem is when it gets to the save I get the error
"Specified method is not supported."
Help will be much appreciated.
Cheers
Thanks Jehof,
So, how would I incorporate my document into that async method?
foreach (var roll in _rollsToAddStudentTo)
{
doc.Element("rolls").Add(new XElement("rollid", roll));
}
WebClient client = new WebClient();
client.OpenWriteCompleted += new OpenWriteCompletedEventHandler(client_OpenWriteCompleted);
client.OpenWriteAsync(new Uri("Rolls.xml", UriKind.Relative));
I have resolved this by changing my logic to below.
using (IsolatedStorageFile isoStore =
IsolatedStorageFile.GetUserStoreForApplication())
{
// Create new file
using (IsolatedStorageFileStream isoStream =
new IsolatedStorageFileStream("Rolls.xml",
FileMode.Create, isoStore))
{
// Write to the Isolated Storage for the user.
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
// Create an XmlWriter.
using (XmlWriter writer = XmlWriter.Create(isoStream, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement("Rolls");
foreach (var roll in _rollsToAddStudentTo)
{
writer.WriteStartElement("roll");
writer.WriteAttributeString("rollid", roll);
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteEndDocument();
}
}
}
The stream you try to save the XDocument is readonly. Cause it is the stream you get passed as argument to your method client_OpenReadCompleted that is registered to the event OpenReadCompleted.
If you want to save your XDocument back via WebClient you need to call one of the OpenWriteAsync-methods.
See also this question: Can I pass a System.Net.MailMessage to a WCF service?
I'd like to add attachments to the mail being sent. Attachments are either files on the local disc, or dynamically created Streams. A WCF contract can contain a Stream, but only when all arguments are of type Stream. So, what is the best way to pass one or more Attachments to a WCF service?
Alright I've solved this myself. The trick here is to convert the attachment to a Base64 encodes string, much the same way email systems do this. I've created a class to handle this. Posted here for others:
[DataContract]
public class EncodedAttachment
{
[DataMember(IsRequired=true)]
public string Base64Attachment;
[DataMember(IsRequired = true)]
public string Name;
/// <summary>
/// One of the System.Net.Mime.MediaTypeNames
/// </summary>
[DataMember(IsRequired = true)]
public string MediaType;
}
public EncodedAttachment CreateAttachment(string fileName)
{
EncodedAttachment att = new EncodedAttachment();
if (!File.Exists(fileName))
throw new FileNotFoundException("Cannot create attachment because the file was not found", fileName);
FileInfo fi = new FileInfo(fileName);
att.Name = fi.Name;
att.MediaType = System.Net.Mime.MediaTypeNames.Text.Plain;
using (FileStream reader = new FileStream(fileName, FileMode.Open))
{
byte[] buffer = new byte[reader.Length];
reader.Read(buffer, 0, (int)reader.Length);
att.Base64Attachment = Convert.ToBase64String(buffer);
}
return att;
}
And on the client side:
public void SendEmail(SmallMessage msg)
{
using (MailMessage message = new MailMessage())
{
message.Body = msg.Body;
message.Subject = msg.Subject;
message.To.Add(new MailAddress(msg.To));
message.From = new MailAddress(msg.From);
foreach (EncodedAttachment att in msg.Attachments)
{
message.Attachments.Add(CreateAttachment(att));
}
SmtpClient client = new SmtpClient();
client.Send(message);
}
}
Attachment CreateAttachment(EncodedAttachment encodedAtt)
{
MemoryStream reader = new MemoryStream(Convert.FromBase64String(encodedAtt.Base64Attachment));
Attachment att = new Attachment(reader, encodedAtt.Name, encodedAtt.MediaType);
return att;
}