I am trying to create an outlook appointment ICS file using C# Asp.net. Below is a sample code that I got from the internet, but it is missing Subject, and also attendees. How can I include that in the code? For example Meeting Subject is: "Finance meeting" Attendees is: Sam#mycompany.com, Andy#mycompany.com, Lisa#mycompany.com
public string MakeHourEvent(string subject, string location, DateTime date, string startTime, string endTime)
{
string filePath = string.Empty;
string path = HttpContext.Current.Server.MapPath(#"\iCal\");
filePath = path + subject + ".ics";
writer = new StreamWriter(filePath);
writer.WriteLine("BEGIN:VCALENDAR");
writer.WriteLine("VERSION:2.0");
writer.WriteLine("PRODID:-//hacksw/handcal//NONSGML v1.0//EN");
writer.WriteLine("BEGIN:VEVENT");
string startDateTime = GetFormatedDate(date)+"T"+GetFormattedTime(startTime);
string endDateTime = GetFormatedDate(date) + "T" + GetFormattedTime(endTime);
writer.WriteLine("DTSTART:" + startDateTime);
writer.WriteLine("DTEND:" + endDateTime);
writer.WriteLine("SUMMARY:" + subject);
writer.WriteLine("LOCATION:" + location);
writer.WriteLine("END:VEVENT");
writer.WriteLine("END:VCALENDAR");
writer.Close();
return filePath;
}
The subject is in the SUMMARY: parameter. DESCRIPTION: is used for the "body".
Attendees are added using ATTENDEE: property, i.e.
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;RSVP=TRUE;CN="John Smith"
;X-NUM-GUESTS=0:mailto:john.smith#domain.com
Related
I have created a calender.ics file attachment to an email being sent to a user. The user then can add the appointment details to their calendar(whichever they choose Outlook, Google calendar, Yahoo! ect.). The .ics file works beautifully if I click on the attachment and download the file, but I've endlessly searched for a decent example where there is a "Add to Calendar" hyperlink or how to go about it with no real success. Is there way to accomplish this in C#? Am I missing something? This is my first stab at Icalender and I'm using StringBuilder() to build out the .ICS file.
var customerMailMessage = new MailMessage(Store.LocationEmail, CustomerEmail);
customerMailMessage.Subject = "Appointment Request For " + ServiceDate.ToShortDateString();
customerMailMessage.Body = body.ToString();
customerMailMessage.IsBodyHtml = true;
//Start of calender invite
DateTime ServiceTime = ServiceDate.Date.Add(StartTime.TimeOfDay);
DateTime DateStart = Convert.ToDateTime(ServiceTime);
string DateFormat = "yyyyMMddTHHmmssZ";
string now = DateTime.Now.ToUniversalTime().ToString(DateFormat);
string Location = string.Format(Store.LocationAddress1 + " " + Store.LocationCity + " " + Store.LocationState + " " + Store.LocationZip);
string Summary = "Calendar reminder";
string Description = "Your appointment details on: " + DateStart;
string FileName = "ServiceAppointment.ics";
Guid guid = Guid.NewGuid();
StringBuilder sb = new StringBuilder();
sb.AppendLine("BEGIN:VCALENDAR");
sb.AppendLine("VERSION:2.0");
sb.AppendLine("PRODID:Domain.com");
sb.AppendLine("CALSCALE:GREGORIAN");
sb.AppendLine("METHOD:REQUEST");
sb.AppendLine("BEGIN:VEVENT");
sb.AppendLine("UID:" + guid);
sb.AppendLine(string.Format("ORGANIZER:MAILTO:{0}", retailStore.LocationEmail));
sb.AppendLine(string.Format("ATTENDEE;CN='{0}, {1}';ROLE=REQ-PARTICIPANT;PARTSTAT=TENTATIVE:MAILTO:{2}", CustomerFirstName, CustomerLastName, CustomerEmail));
sb.AppendLine("DTSTART:" + DateStart.ToUniversalTime().ToString(DateFormat));
sb.AppendLine("DTSTAMP:" + now);
sb.AppendLine("SUMMARY: " + Summary);
sb.AppendLine("LOCATION: " + Location);
sb.AppendLine("DESCRIPTION:" + Description);
sb.AppendLine("PRIORITY:5");
sb.AppendLine("TRANSP:OPAQUE");
sb.AppendLine("END:VEVENT");
sb.AppendLine("END:VCALENDAR");
var calendarBytes = Encoding.UTF8.GetBytes(sb.ToString());
System.IO.MemoryStream stream = new System.IO.MemoryStream(calendarBytes);
Attachment attachment = new Attachment(stream, FileName, "text/calendar");
customerMailMessage.Attachments.Add(attachment);
System.Net.Mime.ContentType contype = new System.Net.Mime.ContentType("text/calendar");
contype.Parameters.Add("method", "REQUEST");
contype.Parameters.Add("name", "ServiceAppointment.ics");
var emailProvider = new EmailProvider();
emailProvider.Send(customerMailMessage, "Request Appointment - Customer - " + CreatedBySource);
Short-answer: A link in the body of the message would not invoke the method[s] on the server-side to convert the ICS file into a Calendar Item for the user's mailbox; especially, considering each platform mentioned (Exchange, Google Mail, Yahoo Mail, etc.) has it's own implementation for doing so (whether via web or client).
As an example, you can read Microsoft's Open Specification for converting an iCal to an Appointment item here.
This below is the code i have. If i comment out the block from object fName to adoc.Close the code works on IIS but if it is not commented out it gives me this error
NullReferenceException: Object reference not set to an instance of an object.]
QuoteProject030117.Controllers.Quote1Controller.Create(Quote1 quote1) +1653
I think iis is having a problem modifying the word doc but i cant find a way to fix it. All of the code works perfectly on Visual Studio when i run it. If anyone could give me a hand would be appreciated. Thank you!
public ActionResult Create([Bind(Include = "id,quotenumber,date,value,won,lost,client,projectdescription,contact")] Quote1 quote1)
{
if (ModelState.IsValid)
{
db.Quote1.Add(quote1);
db.SaveChanges();
string QNsql = #"SELECT quotenumber FROM Quote1 ORDER BY id DESC offset 0 rows fetch next 1 rows only;";
string quoteNum = db.Database.SqlQuery<string>(QNsql).Single();
string Dsql = #"SELECT date FROM Quote1 ORDER BY id DESC offset 0 rows fetch next 1 rows only;";
string date = db.Database.SqlQuery<string>(Dsql).Single();
string Clientsql = #"SELECT client FROM Quote1 ORDER BY id DESC offset 0 rows fetch next 1 rows only;";
string client = db.Database.SqlQuery<string>(Clientsql).Single();
string Contsql = #"SELECT contact FROM Quote1 ORDER BY id DESC offset 0 rows fetch next 1 rows only;";
string contact = db.Database.SqlQuery<string>(Contsql).Single();
string PDessql = #"SELECT projectdescription FROM Quote1 ORDER BY id DESC offset 0 rows fetch next 1 rows only;";
string proDes = db.Database.SqlQuery<string>(PDessql).Single();
System.IO.Directory.CreateDirectory(#"C:\Users\alanf\Documents\CopyTo\" + quoteNum);
System.IO.Directory.CreateDirectory(System.IO.Path.Combine(#"C:\Users\alanf\Documents\CopyTo\" + quoteNum, "Information Recieved From Customer"));
System.IO.Directory.CreateDirectory(System.IO.Path.Combine(#"C:\Users\alanf\Documents\CopyTo\" + quoteNum, "PO Recieved From Customer"));
System.IO.Directory.CreateDirectory(System.IO.Path.Combine(#"C:\Users\alanf\Documents\CopyTo\" + quoteNum, "Project Costing Sheet"));
System.IO.Directory.CreateDirectory(System.IO.Path.Combine(#"C:\Users\alanf\Documents\CopyTo\" + quoteNum, "Quotation Sent To Customer"));
System.IO.Directory.CreateDirectory(System.IO.Path.Combine(#"C:\Users\alanf\Documents\CopyTo\" + quoteNum, "Quotations Recieved From Vendors"));
string fileName = "Quote.docx";
string sourcePath = #"C:\inetpub\wwwroot";
string targetPath = #"C:\Users\alanf\Documents\CopyTo\" + quoteNum + #"\Quotation Sent To Customer";
string sourceFile = System.IO.Path.Combine(sourcePath, fileName);
string destFile = System.IO.Path.Combine(targetPath, fileName);
System.IO.File.Copy(sourceFile, destFile, true);
object fName = System.IO.Path.Combine(targetPath, fileName);
Microsoft.Office.Interop.Word.Application wordApp = new Microsoft.Office.Interop.Word.Application { Visible = false };
Microsoft.Office.Interop.Word.Document aDoc = wordApp.Documents.Open(fName, ReadOnly: false, Visible: false);
aDoc.Activate();
FindAndReplace(wordApp, "<quote>", "" + quoteNum + "");
FindAndReplace(wordApp, "<date>", "" + date + "");
FindAndReplace(wordApp, "<client>", "" + client + "");
FindAndReplace(wordApp, "<contact>", "" + contact + "");
FindAndReplace(wordApp, "<projectdescription>", "" + proDes + "");
aDoc.Close();
Process.Start("explorer.exe", #"C:\Users\alanf\Documents\CopyTo\" + quoteNum);
return RedirectToAction("Index");
}
Office interop from IIS is not supported by Microsoft - see https://social.msdn.microsoft.com/Forums/en-US/1d3923a5-6720-4743-8fa0-c919ff90e4ef/i-cant-open-word-office-on-iis7?forum=worddev and https://support.microsoft.com/en-us/help/257757/considerations-for-server-side-automation-of-office
It would be good to know what line is 1653 as #OmegaMan asked too.
For your scenario, I would recommend looking at a .NET library that supports reading and writing Word documents or doing mail merge. I have used Aspose before, but there are others out there too like GemBox (I haven't used them though).
Also, see this thread Reading doc and docx files using C# without having MS Office installed on server
I'm trying to create a calendar invite and wants to add HTML content inside the attached ICS file.
The following code doesn't work:-
private static byte[] CreateiCal(int current_sequence, string guid, string subject, string location, DateTime startTime, DateTime endTime)
{
var a = new StringBuilder();
var sb = new StringBuilder();
a.Append("BEGIN:VCALENDAR\r\f");
a.Append("VERSION:2.0\r\f");
a.Append("PRODID:-//ince/events//NONSGML v1.0//EN\r\f");
a.Append("TZ:+00\r\f");
a.Append("BEGIN:VEVENT\r\f");
a.Append(String.Format("SEQUENCE:{0}\r\f", sequence.ToString()));
a.Append(String.Format("DTSTART:{0}\r\f", GetFormatedDate(startTime)));
a.Append(String.Format("DTEND:{0}\r\f", GetFormatedDate(endTime)));
a.Append(String.Format("LOCATION:{0}\r\f", location));
a.Append(String.Format("UID:{0}\r\f", guid));
a.Append(String.Format("SUMMARY:{0}\r\f", subject));
sb.Append("Sample Text1 \n");
sb.Append("Sample Text2 \n");
sb.Append(string.Format("Sample Text3 <a href='{0}'>LINK</a> \n", "www.google.com"));
sb.Append("Sample Text4 \n");
a.Append(String.Format("DESCRIPTION;X-ALT-DESC;FMTTYPE=text/html:"+sb.ToString() + "\r\f"));
a.Append((string.Format("ORGANIZER:MAILTO:{0}\r\f", "mailID#corporate.com")));
a.Append((string.Format("ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=FALSE;X-NUM-GUESTS=0:mailto:{0}\r\f", "mailID2#corporate.com")));
a.Append(String.Format("X-MICROSOFT-CDO-APPT-SEQUENCE:{0}\r\f", current_sequence.ToString()));
a.Append("BEGIN:VALARM\r\f");
a.Append("TRIGGER:-PT15M\r\f");
a.Append("REPEAT:2\r\f");
a.Append("DURATION:PT15M\r\f");
a.Append("ACTION:DISPLAY\r\f");
a.Append("DESCRIPTION:Reminder\r\f");
a.Append("END:VALARM\r\f");
a.Append("END:VEVENT\r\f");
a.Append("END:VCALENDAR\r\f");
byte[] b = Encoding.ASCII.GetBytes(a.ToString());
return b;
}
Can anyone please share some inputs on how to achieve this?
Thanks in advance.
I finally managed to resolved this.
Please find below the solution:-
private static byte[] CreateiCal(int current_sequence, string guid, string subject, string location, DateTime startTime, DateTime endTime)
{
var a = new StringBuilder();
int sequence = current_sequence + 1;
a.Append("BEGIN:VCALENDAR\r\f");
a.Append("VERSION:2.0\r\f");
a.Append("PRODID:-//ince/events//NONSGML v1.0//EN\r\f");
a.Append("TZ:+00\r\f");
a.Append("BEGIN:VEVENT\r\f");
a.Append(String.Format("SEQUENCE:{0}\r\f", sequence.ToString()));
a.Append(String.Format("DTSTART:{0}\r\f", GetFormatedDate(startTime)));
a.Append(String.Format("DTEND:{0}\r\f", GetFormatedDate(endTime)));
a.Append(String.Format("LOCATION:{0}\r\f", location));
a.Append(String.Format("UID:{0}\r\f", guid));
a.Append(String.Format("SUMMARY:{0}\r\f", subject));
string desc = File.ReadAllText("Sample.html", Encoding.GetEncoding("iso-8859-1")); // iso-8859-1 : HTML contains French characters
a.Append("X-ALT-DESC;FMTTYPE=text/html:" + desc + "\r\f");
a.Append(String.Format("DESCRIPTION:{0}\r\f", desc));
a.Append(String.Format("X-MICROSOFT-CDO-APPT-SEQUENCE:{0}\r\f", current_sequence.ToString()));
a.Append("BEGIN:VALARM\r\f");
a.Append("TRIGGER:-PT15M\r\f");
a.Append("REPEAT:2\r\f");
a.Append("DURATION:PT15M\r\f");
a.Append("ACTION:DISPLAY\r\f");
a.Append("DESCRIPTION:Reminder\r\f");
a.Append("END:VALARM\r\f");
a.Append("END:VEVENT\r\f");
a.Append("END:VCALENDAR\r\f");
byte[] b = Encoding.UTF8.GetBytes(a.ToString());
return b;
}
NOTE:- I've to minify the HTML before Saving it in the "Sample.html" file.
Hope it helps others!!
The Problem
I have a Visual Studios 2015 Console Application with the Microsoft.Exchange.WebServices v2.2.0 NuGet package installed. I'm trying to create an appointment, update it, and cancel it while retaining the correct Timezone in the "When" string generated automatically in the calendar invite body. Currently, the initial creation has the correct timezone, but any subsequent updates cause the timezone to revert to UTC.
Note: We have an Exchange 2010 server and use Outlook 2013 clients.
Code Sample
using System;
using System.Globalization;
using Microsoft.Exchange.WebServices.Data;
namespace EWSTesting
{
class Program
{
private const string EmailServer = ""; //replace with your Exchange server
private const string EmailAddress = ""; //replace with your email
static void Main(string[] args)
{
Console.WriteLine("Current Timezone: " + TimeZoneInfo.Local.DisplayName);
var exchangeService = new ExchangeService(ExchangeVersion.Exchange2010, TimeZoneInfo.Local)
{
PreferredCulture = new CultureInfo("en-US"),
Url = new Uri(EmailServer),
UseDefaultCredentials = true
};
Console.WriteLine("exchangeService.TimeZone.DisplayName: " + exchangeService.TimeZone.DisplayName);
var startDate = DateTime.Today;
var endDate = startDate.AddHours(1);
//Create initial appointment
var appointment = new Appointment(exchangeService)
{
Subject = "Testing Appointments",
Body = "Testing Appointments Body",
Location = "Test Location",
LegacyFreeBusyStatus = LegacyFreeBusyStatus.Busy,
Sensitivity = Sensitivity.Private,
Start = startDate,
End = endDate
};
appointment.OptionalAttendees.Add(EmailAddress);
appointment.Save(SendInvitationsMode.SendOnlyToAll);
Console.WriteLine("exchangeService.TimeZone.DisplayName: " + exchangeService.TimeZone.DisplayName);
var appointmentId = appointment.Id;
Console.WriteLine("Pause to check inbox 'When' value on invite");
Console.ReadLine();
appointment = Appointment.Bind(exchangeService, appointmentId);
appointment.Load(new PropertySet(PropertySet.FirstClassProperties)
{
AppointmentSchema.StartTimeZone,
AppointmentSchema.EndTimeZone,
AppointmentSchema.TimeZone
});
appointment.Body = "Body Updated Successfully";
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendOnlyToAll);
Console.WriteLine("exchangeService.TimeZone.DisplayName: " + exchangeService.TimeZone.DisplayName);
Console.WriteLine("appointment.StartTimeZone.DisplayName: " + appointment.StartTimeZone.DisplayName);
Console.WriteLine("appointment.EndTimeZone.DisplayName: " + appointment.EndTimeZone.DisplayName);
Console.WriteLine("appointment.TimeZone: " + appointment.TimeZone);
Console.WriteLine();
Console.WriteLine("Pause to check updated inbox 'When' value on invite");
Console.ReadLine();
appointment = Appointment.Bind(exchangeService, appointmentId);
appointment.Load(new PropertySet(PropertySet.FirstClassProperties)
{
AppointmentSchema.StartTimeZone,
AppointmentSchema.EndTimeZone,
AppointmentSchema.TimeZone
});
Console.WriteLine("exchangeService.TimeZone.DisplayName: " + exchangeService.TimeZone.DisplayName);
Console.WriteLine("appointment.StartTimeZone.DisplayName: " + appointment.StartTimeZone.DisplayName);
Console.WriteLine("appointment.EndTimeZone.DisplayName: " + appointment.EndTimeZone.DisplayName);
Console.WriteLine("appointment.TimeZone: " + appointment.TimeZone);
appointment.CancelMeeting();
Console.WriteLine("Appointment Deleted");
Console.ReadLine();
}
}
}
The Results of the above code
Initial Invite (Correct Timezone)
Updated Appointment (Incorrect Timezone in body)
Appointment Cancellation (Incorrect Timezone in body)
Console Result of code provided
What I'm Looking For
I do not need this additional "When" (underlined in red in pictures above) to be appended to the body of the invite. Either I would like to completely remove it (preferred) or I would like to correct it in any updates.
It appears that the issue is a bug in the EWS 2.2.0 DLL, the timezone SOAP headers are not being added to the Update() and CancelMeeting() Exchange transactions. The code below resolves this issue by manually appending the correct header.
For Update():
exchangeService.OnSerializeCustomSoapHeaders += service_OnSerializeCustomSoapHeaders;
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendOnlyToAll);
exchangeService.OnSerializeCustomSoapHeaders -= service_OnSerializeCustomSoapHeaders;
For CancelMeeting():
exchangeService.OnSerializeCustomSoapHeaders += service_OnSerializeCustomSoapHeaders;
appointment.CancelMeeting();
exchangeService.OnSerializeCustomSoapHeaders -= service_OnSerializeCustomSoapHeaders;
Event Implementation:
static void service_OnSerializeCustomSoapHeaders(XmlWriter writer)
{
writer.WriteRaw(Environment.NewLine + " <t:TimeZoneContext><t:TimeZoneDefinition Id=\"" + TimeZoneInfo.Local.StandardName + "\"/></t:TimeZoneContext>" + Environment.NewLine);
}
I have a Function that, when activated, iterates through the Crystal Report it is attached to, copies it to a pdf, then mails the entirety of it to a client once per identifying field.
They want to receive a pdf of records grouped by ID, for each ID in the Report, omitting some of a specific ID. I have no idea how to break the Report down into smaller Reports, though, or even where to begin if that's possible in the first place.
I am Creating Each Pdf for individual user and saving to my Google Driver also emailing using SendGrip Api.
I have used this code inside page.Aspx -> aspx.cs file.
//0. Here i am getting list users as an Object:
OpsManagementController OM = new OpsManagementController();
//1. Getting Users List:
var result = OM.UsersGetforInvoice();
//2. Creating folder for Invoices:
string folderName = #"D:\Google Drive\MonthlyInvoices";
string fileName = ("Invoices_" + DateTime.Now.ToString("yyyy-MM-dd").ToString());
string pathString = System.IO.Path.Combine(folderName, fileName);
System.IO.Directory.CreateDirectory(pathString);
string folderNameEmail = #"D:\Google Drive\MonthlyInvoices\Email";
string fileNameEmail = ("Invoices_" + DateTime.Now.ToString("yyyy-MM-dd").ToString());
string pathStringEmail = System.IO.Path.Combine(folderNameEmail, fileNameEmail);
System.IO.Directory.CreateDirectory(pathStringEmail);
//3. Generating invoices by user name:
for (int i = 0; i < result.UserDetail.Count; i++)
{
var userId = result.UserDetail[i].UserID;
var userEmail = result.UserDetail[i].Email;
var userName = result.UserDetail[i].FullName;
userName = userName.Replace(#"C\O", "CO");
userName = userName.Replace(#"C/O", "CO");
// Directories for reports:
var invoicePath = "D:/Google Drive/MonthlyInvoices/" + fileName + "/" + userId + " " + userName + ".pdf";
var invoicePath_email = "D:/Google Drive/MonthlyInvoices/Email/" + fileNameEmail + "/" + userId + " " + userName + ".pdf";
report2.SetParameterValue("UserID", result.UserDetail[i].UserID);
report2.ExportToDisk(ExportFormatType.PortableDocFormat, invoicePath);
// using sendgrip Api :
EmailUtils.SendEmail_Att(
new string[] { userEmail }, //TO : userEmail
new string[] { "email#gmail.com" }, //
invoiceSubject,
invoiceBody,
invoicePath_email
);
}