How do I create an Outlook "appointment" with DDay.iCal? - c#

I'm using the DDay library to create an iCal event, so that users of my site can add something to their calendar.
I want them to add an appointment as opposed to a meeting request in Office 2010 (and hopefully others too). When I use the library and set the method to PUBLISH, it does appear as an appointment, but it reports that the meeting cannot be found in the calendar. Then when I click no response required, the item gets deleted and doesn't stay in their calendar.
If I change the method to REQUEST, it shows up as a meeting request. This would an okay second best option, but the 'to' field is blank. If that's the best I can do, how can I set the 'to' field? I guess I would have them respond to themselves.
private static string CreateCalendarEvent(
string title, string body, DateTime startDate, double duration,
string location, string organizer, string eventId, bool allDayEvent)
{
// mandatory for outlook 2007
if(String.IsNullOrEmpty(organizer))
throw new Exception("Organizer provided was null");
var iCal = new iCalendar
{
Method = "PUBLISH",
Version = "2.0"
};
// "REQUEST" will update an existing event with the same UID (Unique ID) and a newer time stamp.
//if (updatePreviousEvent)
//{
// iCal.Method = "REQUEST";
//}
var evt = iCal.Create<Event>();
evt.Summary = title;
evt.Start = new iCalDateTime(startDate);
evt.Duration = TimeSpan.FromHours(duration);
evt.Description = body;
evt.Location = location;
evt.IsAllDay = allDayEvent;
evt.UID = String.IsNullOrEmpty(eventId) ? new Guid().ToString() : eventId;
evt.Organizer = new Organizer(organizer);
evt.Alarms.Add(new Alarm
{
Duration = new TimeSpan(0, 15, 0),
Trigger = new Trigger(new TimeSpan(0, 15, 0)),
Action = AlarmAction.Display,
Description = "Reminder"
});
return new iCalendarSerializer().SerializeToString(iCal);
}

When I set the organizer to an email address, as opposed to a test string, it worked fine. I had written all of this up, so I thought I'd share it in case anyone else had the same problem

My app stopped working when the exchange server was upgraded to Outlook 2010 from 2003. Before the upgrade PUBLISH worked fine but now I had to change to REQUEST
Thanks for the article

Related

C# Google Calendar

Everything is already setup on the google calendar api (not sure if i configure correctly or i missed something out)
I created a c# console application,that writes an appointment to google calendar.
What i want to achieve is that , i want to get all the user's or subscriber who subscribe to my app, so what i can write
on there calendar if there is an event.
What configuration do i need?
That's actually a multi-domain problem.
Some questions to consider:
What calender is it? A public one?
How do they subscribe with your console?
What happens when the software shuts down, crashes, etc?
Do you need to know the event after it was written to the calendars?
If not, do new subscriber get previously added calender entries?
Other than that, you should have a look at Webclient. Then we have here the reference for the Google Calendar API. The main request method you're searching is this one: Events Insert. With that, we can craft our own inseration variant.
```
private readonly List<string> _calendarIDs;
/* lets assume you imported webrequests and you're going to write a method
* Further we assume, you have a List of calendars as object attribute
*/
public void InsertEntry(DateTime start, DateTime end,
string title, string description) {
using(var client = new WebClient()) {
var epochTicks = new DateTime(1970, 1, 1);
var values = new NameValueCollection();
values["attachements[].fileUrl"] = "";
values["attendees[].email"] = "";
values["end.date"] = end.ToString("yyyy-MM-dd");
values["end.dateTime"] = (end - epoch).Seconds;
values["reminders.overrides[].minutes"] = 0;
values["start.date"] = start.ToString("yyyy-MM-dd");
values["start.dateTime"] = (start - epoch).Seconds;
values["summary"] = title; // This is the calendar entrys title
values["description"] = description;
foreach(string calendarID in _calendarIDs) {
var endpoint = String.Format("https://www.googleapis.com/calendar/v3/calendars/{0}/events", calendarID)
var response = client.UploadValues(endpoint, values);
var responseString = Encoding.Default.GetString(response);
}
}
This is a minimal example and the api has a lot of endpoints and parameter. You should have a deep look into it, maybe you find more useful parameter.
Below is the sample code,
GoogleCalendarUtils utils = new GoogleCalendarUtils();
ArrayList months = /* the list of months*/;
// Update the content window.
foreach( ThistleEventMonth month in months )
{
foreach( ThistleEvent thistleEvent in month.ThistleEvents )
{
utils.InsertEntry( thistleEvent );
}
}

How to trigger a SSRS Subscription based on an event?

Is there a way by which I can trigger a SSRS subscription (Time based) whenever there is an event like file created in a shared folder? Can we do it with powershell or C#?
Is there a out of box feature available in SSRS (though I don't think there is any)?
I am using SQL Server 2008 R2.
Yes, we do something like this here. You can use the FireSubscription function of the Reporting Services web services to trigger a subscription. Here's a detailed explanation of how to set it up:
Firing a Reporting Services Subscription
You can use the FileSystemWatcher to tell when your file is dropped and then fire the subscription off. It's asynchronous though so you don't get notification if the report was sent successfully... only that it was successfully queued up. Also you first modify the parameters of the subscription before you fire it, so you have to make sure that you don't have more than one program to trigger the subscription or it might end up tripping over itself.
Another slightly more complicated way to do it is to use the Render function to generate a report and then have your program manage the emailing.
Render Function
This way you don't have to create a dummy subscription and you'll know immediately if it was sent successfully with the correct parameters.
One final note... if you have the Enterprise Edition (which you probably don't), it comes with Data Driven Report Subscriptions, which you could use to trigger a subscription:
Creating a Data-Driven Subscription
Here i have used timely subscription , i had requirement to generate report
on some button click, so i created subscription which will fire after one minute and generate PDF report.
And I got all help from this article :
http://odetocode.com/articles/114.aspx
You need to add webservice reference of webservice provided by SSRS
http://mymachine/ReportServer/ReportService2010.asmx
Here #"\MyMachineName\Share", is path where my pdf was stored
(PATH:The folder path or UNC file share path to which to save the report.
https://msdn.microsoft.com/en-us/library/ms154020.aspx)
So you can call generate subscription as per your need on file created.
using Test_WebProject.ReportService2010;
private static ExtensionSettings GetExtensionSettings()
{
ParameterValue[] extensionParams = new ParameterValue[7];
for (int i = 0; i < extensionParams.Length; i++)
extensionParams[i] = new ParameterValue();
extensionParams[0].Name = "FILENAME";
extensionParams[0].Value = "Test1#TimeStamp";
extensionParams[1].Name = "FILEEXTN";
extensionParams[1].Value = "true";
extensionParams[2].Name = "PATH";
extensionParams[2].Value = #"\\MyMachineName\Share";
extensionParams[3].Name = "RENDER_FORMAT";
extensionParams[3].Value = "PDF";
extensionParams[4].Name = "WRITEMODE";
extensionParams[4].Value = "None"; //"Overwrite ";// "AutoIncrement";
extensionParams[5].Name = "USERNAME";
extensionParams[5].Value = "gmd";
extensionParams[6].Name = "PASSWORD";
extensionParams[6].Value = "password123";
ExtensionSettings extensionSettings = new ExtensionSettings();
extensionSettings.Extension = "Report Server FileShare"; // EXTENSION_FILESHARE;
extensionSettings.ParameterValues = extensionParams;
return extensionSettings;
}
static void generateSubscription()
{
string report = #"/MyReports/TestSSRSSubscrptionReport";
string description = "My Test subscription2010";
string eventType = "TimedSubscription";
ExtensionSettings extSettings = GetExtensionSettings();
List<ReportService2010.ParameterValue> parameters = new List<ReportService2010.ParameterValue>();
parameters.Add(new ReportService2010.ParameterValue() { Name = "EmployeeKey", Value = "9" });
parameters.Add(new ReportService2010.ParameterValue() { Name = "SelectedColumn", Value = "EmployeeKey" });
parameters.Add(new ReportService2010.ParameterValue() { Name = "ParamSelectedColumns", Value = "FirstName" });
parameters.Add(new ReportService2010.ParameterValue() { Name = "ParamSelectedColumns", Value = "LastName" });
NetworkCredential credentials = new NetworkCredential("gmd", "password123");
ReportService2010.ReportingService2010 rs = new ReportService2010.ReportingService2010();
rs.Credentials = credentials; // System.Net.CredentialCache.DefaultCredentials;
DateTime topDatetime = DateTime.Now;
topDatetime = topDatetime.AddMinutes(1);
string scheduleXml = "<ScheduleDefinition><StartDateTime>";
scheduleXml += topDatetime.ToShortDateString() + " " + topDatetime.ToShortTimeString();
scheduleXml += "</StartDateTime></ScheduleDefinition>";
string sid = rs.CreateSubscription(report, extSettings, description, eventType, scheduleXml, parameters.ToArray());
}
You could create a windows service that uses FileSystemWatcher (https://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher%28v=vs.110%29.aspx) and then just trigger your job on the changed event.

Write appointment without user confirmation using WinRT Appointments API

I'm experimenting with the new WinRT Appointments API in Windows 8.1, based on a sample provided on the MSDN website of Microsoft: http://code.msdn.microsoft.com/windowsapps/Appointments-API-sample-2b55c76e
It works great and I can add appointments without a hassle, but there's always a confirmation by the user involved when using the method ShowAddAppointmentAsync from the Windows.ApplicationModel.Appointments.AppointmentManager namespace, which shows the Appointments provider Add Appointment UI.
I'm looking for a solution to add a larger collection of appointments in the default Windows 8 calendar, WITHOUT the confirmation for each individual appointment in the collection. Is there a way to get around this and bulk insert appointments? Maybe the Windows Live SDK?
Its true that the API prompts the user before saving, but there is a provision to achieve this.
var appointment = new Windows.ApplicationModel.Appointments.Appointment();
appointment.details = "This is a dummy appointment";
appointment.reminder = 15000;
appointment.subject = "TEST APPPOINTMENT";
var x = new Windows.ApplicationModel.Appointments.AppointmentManager.requestStoreAsync(Windows.ApplicationModel.Appointments.AppointmentStoreAccessType.appCalendarsReadWrite).done(function (apppointmentStore) {
apppointmentStore.createAppointmentCalendarAsync("TEST CALENDAR").done(function (calendar) {
calendar.saveAppointmentAsync(appointment);
});
})
Here you're an example to do it using C#
private AppointmentCalendar currentAppCalendar;
private AsyncLazy<AppointmentStore> lazyAppointmentStore = new AsyncLazy<AppointmentStore>(async () =>
{
var appStore = await AppointmentManager.RequestStoreAsync(AppointmentStoreAccessType.AppCalendarsReadWrite);
return appStore;
});
private AppointmentStore AppStore { get { return lazyAppointmentStore.Value.Result; } }
public AppointmentService()
{
}
public async Task CreateCalendar()
{
IReadOnlyList<AppointmentCalendar> appCalendars =
await AppStore.FindAppointmentCalendarsAsync(FindAppointmentCalendarsOptions.IncludeHidden);
AppointmentCalendar appCalendar = null;
// Apps can create multiple calendars. Here app creates only one.
if (appCalendars.Count == 0)
{
appCalendar = await AppStore.CreateAppointmentCalendarAsync(Constants.CalendarName);
}
else
{
appCalendar = appCalendars[0];
}
appCalendar.OtherAppReadAccess = AppointmentCalendarOtherAppReadAccess.Full;
appCalendar.OtherAppWriteAccess = AppointmentCalendarOtherAppWriteAccess.SystemOnly;
// This app will show the details for the appointment. Use System to let the system show the details.
appCalendar.SummaryCardView = AppointmentSummaryCardView.App;
await appCalendar.SaveAsync();
currentAppCalendar = appCalendar;
}
public async Task<bool> CreateNewAppointment(Data.Schemas.Task task)
{
if (null == task)
throw new ArgumentNullException("task");
Appointment newAppointment = new Appointment();
this.SaveAppointmentData(task, newAppointment);
try
{
// Show system calendar to the user to be edited
string appointmentId = await AppointmentManager.ShowAddAppointmentAsync(newAppointment, Windows.Foundation.Rect.Empty);
return ! string.IsNullOrWhiteSpace(appointmentId);
// Just save the appointment
// await currentAppCalendar.SaveAppointmentAsync(newAppointment);
// return true;
}
catch
{
return false;
}
}
Check my post, to know more about AsyncLazy.
I hope this help you.
Regards.
Juanlu
This is not possible, by using the WinRT appointments API.
A user interaction is always required. It was a design decision by MS that some actions require user interaction and this is one of it.
As stated by #Ken Tucker, you can use the windows live api to create appointments but this requires the user of your app to sing in to windows live and grat it the required permissions.

Error Sending Bulk Calendar Appointments Dynamically Using EWS and C#

I'm developing an outbound call system that shows the user the next company to call with a means to add an appointment for a selected sales rep.
I initially programmed the appointment to be sent in real time as the user saves the current data and move to the next lead. However, there was a significant delay while the appointment was being sent to the email account on Office 365. So I decided to create a small app to send them in batch every 15 minutes.
This works great for the first three appointments, but then I get an error on the 4th appointment:
The Autodiscover service couldn't be located.
I'm suspecting this is something like a hosting company stopping a mailbox from being used for bulk email and there is a limit. Is there something within EWS to allow more than three at a time? Or do I need to amend my code?
Here is my code for sending the appointments:
const string o365Server = "mydomain.co.uk";
var appointmenntList = AppointmentList.GetAppointnmetsToSend();
ExchangeService service = new ExchangeService();
foreach (var appointment in appointmenntList)
{
Console.WriteLine(appointment.IntLeadID);
service.Credentials = new WebCredentials(appointment.StrSalesRepEmail, apointment.StrSalesRepEmailPassword, o365Server);
service.AutodiscoverUrl(appointment.StrSalesRepEmail, RedirectionCallback);
Appointment app = new Appointment(service);
app.Subject = "ASH Waste Appointment with " + appointment.StrLeadAppointmentContact;
app.Body = appointment.StrLeadAppointmentNotes;
app.Start = appointment.DtLeadAppointmentDate;
app.End = app.Start.AddHours(1);
app.Location = appointment.StrLeadAppointmentLocation;
app.RequiredAttendees.Add(appointment.StrSalesRepEmail);
app.ReminderMinutesBeforeStart = 60;
app.Save(SendInvitationsMode.SendOnlyToAll);
Console.WriteLine(appointment);
}
static bool RedirectionCallback(string url)
{
return url.ToLower().StartsWith("https://");
}
I could set the app to do only three at a time and run the app every 2 minutes. Although this would be enough for our sales team, it is limiting the operation.
Any suggestions?
Edit:
Here's the full error:
An unhandled exception of type
Microsoft.Exchange.WebServices.Data.AutodiscoverLocalException'
occurred in Microsoft.Exchange.WebServices.dll
and it occurs on this line of code:
service.AutodiscoverUrl(appointment.StrSalesRepEmail, RedirectionCallback);
I can batch 4 calendar items when using the CreateItems method instead. Have you tried that? Here's some sample code.
public static Collection<ItemId> BatchCreateCalendarItems(ExchangeService service)
{
// These are unsaved local instances of an Appointment object.
// Despite the required parameter of an ExchangeService object (service), no call
// to an Exchange server is made when the objects are instantiated.
// A call to the Exchange server is made when the service.CreateItems() method is called.
Appointment appt1 = new Appointment(service);
Appointment appt2 = new Appointment(service);
Appointment appt3 = new Appointment(service);
Appointment appt4 = new Appointment(service);
// Set the properties for a single instance appointment
appt1.Subject = "Appt1";
appt1.Body = "Appt1";
appt1.Start = DateTime.Now.AddDays(1);
appt1.End = appt1.Start.AddHours(3);
appt1.Location = "My office";
appt1.ReminderMinutesBeforeStart = 30;
// Set the properties for a single instance appointment
appt2.Subject = "Appt2";
appt2.Body = "Appt2";
appt2.Start = DateTime.Now.AddDays(1);
appt2.End = appt1.Start.AddHours(4);
appt2.Location = "My office";
appt2.ReminderMinutesBeforeStart = 30;
// Set the properties for a single instance appointment
appt3.Subject = "Appt3";
appt3.Body = "Appt3";
appt3.Start = DateTime.Now.AddDays(1);
appt3.End = appt1.Start.AddHours(5);
appt3.Location = "My office";
appt3.ReminderMinutesBeforeStart = 30;
// Set the properties for a single instance appointment
appt4.Subject = "Appt4";
appt4.Body = "Appt4";
appt4.Start = DateTime.Now.AddDays(1);
appt4.End = appt1.Start.AddHours(6);
appt4.Location = "My office";
appt4.ReminderMinutesBeforeStart = 30;
// Add the appointment objects to a collection
Collection<Appointment> calendarItems = new Collection<Appointment>() { appt1, appt2, appt3, appt4 };
// Instantiate a collection of item ids to populate from the values that are returned by the Exchange server.
Collection<ItemId> itemIds = new Collection<ItemId>();
// Send the batch of appointment objects.
// Note that multiple calls to the Exchange server may be made when appointment objects have attachments.
// Note also that the item collection passed as the first parameter to CreateItems will have their ids set on return.
ServiceResponseCollection<ServiceResponse> response = service.CreateItems(calendarItems,
WellKnownFolderName.Calendar,
MessageDisposition.SendAndSaveCopy,
SendInvitationsMode.SendToAllAndSaveCopy);
if (response.OverallResult == ServiceResult.Success)
{
Console.WriteLine("All appointments and meetings sucessfully created.");
}
// Collect the item ids from the created calendar items.
foreach (Appointment appt in calendarItems)
{
itemIds.Add(appt.Id);
}
int counter = 1;
// Show the ids and errors for each message
foreach (ServiceResponse resp in response)
{
// Note that since item ids are long, show only 5 characters.
Console.WriteLine("Result (message {0}), id {1}: {2}", counter, itemIds[counter - 1].ToString().Substring(0, 5), resp.Result);
Console.WriteLine("Error Code: {0}", resp.ErrorCode);
Console.WriteLine("ErrorMessage: {0}\r\n", resp.ErrorMessage);
counter++;
}
// Return the collection of item ids
return itemIds;
}
Well, it isn't that you're getting blocked from sending a specific number of appointments, it's that Autodiscover is failing. That error is one that the EWS Managed API likes to return for all kinds of scenarios, so it doesn't tell me for sure why it is failing. It could be a temporary network issue, or it could be that the Autodiscover servers are throttling you. I'm not sure offhand if Autodiscover does any throttling, but it's certainly possible.
To see why it's failing, you could enable tracing with all of the Autodiscover-related trace flags turned on and see what kinds of errors are coming back.

Implementing Outlook 2010's group by conversation using EWS and Exchange 2007

We're using EWS to generate some analytics on some of our mailboxes.
Part of this is getting a count/name/start/end of conversations. A conversation being analogous to the way Outlook 2010 shows them when grouping by conversation.
I was hoping to be able to use the ConversationId to group items, but that seems to be an Exchange 2010-only feature.
I can group by subject within a folder to get a simple idea of threads... however this does not handle split conversations, as Outlook 2010 does - specifically, it doesn't handle bringing in the replies that are in the sent items (these are important to us - we can't get good metrics without also looking at replies).
My current code for getting thread info looks like this:
private IEnumerable<EmailThread> GetThreads(Folder folder)
{
var view = new ItemView(int.MaxValue) {PropertySet = new PropertySet(BasePropertySet.IdOnly)};
// view.PropertySet.Add(ItemSchema.ConversationId); - Can't use this as we're stuck on Exchange 2007 !!!
view.PropertySet.Add(ItemSchema.Subject);
view.PropertySet.Add(ItemSchema.DateTimeReceived);
var grouping = new Grouping(ItemSchema.Subject, SortDirection.Descending, ItemSchema.DateTimeReceived, AggregateType.Maximum);
var groupResults = folder.FindItems(view, grouping);
return groupResults.Select(x => new EmailThread
{
Name = x.Items.First().Subject,
Items = x.Items.Count,
StartDate = x.Items.Last().DateTimeReceived, // Assume last in thread is first email
EndDate = x.Items.First().DateTimeReceived // Assume first in thread is most recent
});
}
I am hoping someone knows of a neat way to efficiently get information on replies that constitute part of a conversation?
You can fetch the ConversationId and the ConversationIndex via extended properties:
private static readonly ExtendedPropertyDefinition ConversationIdProperty = new ExtendedPropertyDefinition(0x3013, MapiPropertyType.Binary);
private static readonly ExtendedPropertyDefinition ConversationIndexProperty = new ExtendedPropertyDefinition(0x0071, MapiPropertyType.Binary);
var items = service.FindItems(WellKnownFolderName.Inbox, new ItemView(512) { PropertySet = new PropertySet(BasePropertySet.FirstClassProperties,
ConversationIdProperty, ConversationIndexProperty)});
Both are binary properties. Their content is described in great detail here:
[MS-OXOMSG]: E-Mail Object Protocol Specification, section 2.2.1.2 and 2.2.1.3.
The properties themselves are defined in [MS-OXPROPS]: Exchange Server Protocols Master Property List.

Categories

Resources