I am currently working on an app that checks all appointments on an individual email, it can update appointments, delete them, create new etc. This part is working already.
I am now trying to put all appointment starting from a specified last modified time into a list, and here is my issue. Code:
public void getByModifiedDate(CalendarFolder calendar)
{
bool found = false;
string date;
Console.WriteLine("Modified date: ");
date = Convert.ToString(Console.ReadLine());
List<TAppointments> tempList = new List<TAppointments>();
var appointments = getAppointments(calendar);
//loop through every appts on calendar
foreach (Appointment a in appointments)
{
if (a.LastModifiedTime.ToString() == date)
{
TAppointments app = new TAppointments(a.ICalUid.ToString(), a.Subject.ToString(), a.Start.ToString(), a.End.ToString(), a.LastModifiedTime.ToString());
currentApp = app;
tempList.Add(app);
found = true;
}
}
if (!found)
{
Console.WriteLine("No appointment found.");
}
printAppointments(tempList);
}
Here I can only find a single item, because well that's what I coded. Is there a way to specified a starting date? Something like lastmodifiedtime >= date? I've made some researched and did not find anything.
I fixed my case. I simply converted my specified date to DateTime, then I could compare it properly, as seen below. I hope it helps some people who has the same issue.
foreach (Appointment a in appointments)
{
if (a.LastModifiedTime >= Convert.ToDateTime(date))
{
i++;
TAppointments app = new TAppointments(a.ICalUid.ToString(), a.Subject.ToString(), a.Start.ToString(), a.End.ToString(), a.LastModifiedTime.ToString());
currentApp = app;
tempList.Add(app);
found = true;
}
}
Related
I am trying to add Appointments to my UWP App.
I have successfully set up the Appointment but the result for created Appointment Id is empty which means that the appointment is not created.
Following is my Code:
public static Rect GetElementRect(FrameworkElement element)
{
GeneralTransform transform = element.TransformToVisual(null);
Point point = transform.TransformPoint(new Point());
return new Rect(point, new Size(element.ActualWidth, element.ActualHeight));
}
private async Task<ManagerResponseModel> AddAppointmentToOutlookCalendar(ViewingSummaryModel model)
{
ManagerResponseModel result = new ManagerResponseModel();
//Add appointment if assigned to the same user
if(model.HousingOfficerId == AppSession.LoggedinUserId)
{
// Create an Appointment that should be added the user's appointments provider app.
var appointment = new Appointment();
//Populate Viewing Data in appointment
appointment.Subject = string.Format("Viewing at {0}", model.PropertyAddress);
appointment.Location = (string.IsNullOrWhiteSpace(model.PropertyAddress)) ? "NA" : model.PropertyAddress;
appointment.BusyStatus = AppointmentBusyStatus.Tentative;
appointment.Sensitivity = AppointmentSensitivity.Public;
appointment.AllDay = false;
//var timeZoneOffset = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now);
var date = GeneralHelper.GetCombinedDateTimeStringForViewing(model.ViewingDate, model.FormattedTime);
//var startTime = new DateTimeOffset(date.Year, date.Month, date.Day, date.Hour, date.Minute, 0, TimeZoneInfo.Local.BaseUtcOffset);
appointment.StartTime = date;
appointment.Details = string.Format("Customer: {0}", model.CustomerName) + "\r"
+ string.Format("Housing Officer: {0}", (string.IsNullOrWhiteSpace(model.AssignedTo)) ? "NA" : model.AssignedTo) + "\r"
+ string.Format("Address: {0}", model.PropertyAddress) + "\r"
+ string.Format("Created by: {0}", (string.IsNullOrWhiteSpace(model.CreatorName)) ? "You" : model.CreatorName);
// Get the selection rect of the button pressed to add this appointment
var rect = GetElementRect(this.Frame as FrameworkElement);
string appointmentId = string.Empty;
// ShowAddAppointmentAsync returns an appointment id if the appointment given was added to the user's calendar.
// This value should be stored in app data and roamed so that the appointment can be replaced or removed in the future.
// An empty string return value indicates that the user canceled the operation before the appointment was added.
if (!string.IsNullOrWhiteSpace(model.OutlookIdentifier))
{
appointmentId = await AppointmentManager.ShowReplaceAppointmentAsync(model.OutlookIdentifier, appointment, rect, Placement.Default, date);
/*Appointment doesn't exist on this system, try to add a new one*/
if (string.IsNullOrWhiteSpace(appointmentId))
{
appointmentId = await AppointmentManager.ShowAddAppointmentAsync(appointment, rect, Windows.UI.Popups.Placement.Default);
}
}
else
{
appointmentId = await AppointmentManager.ShowAddAppointmentAsync(appointment, rect, Windows.UI.Popups.Placement.Default);
}
model.OutlookIdentifier = appointmentId;
result.isSuccessful = string.IsNullOrWhiteSpace(appointmentId);
result.responseObject = model;
}
else
{
result.isSuccessful = true;
result.responseObject = model;
}
return result;
}
The following line:
appointmentId = await AppointmentManager.ShowAddAppointmentAsync(appointment, rect, Windows.UI.Popups.Placement.Default);
returns empty only if user cancels the operation or there is some other issue resulting in failure in creation of appointment. I am not cancelling the operation, nor is there any raised exception so I have no idea what am I doing wrong here.
I figured it out after reading a related thread on MSDN. It was a really stupid mistake. I had forgotten to add the Appointments capability in my Package.appxmanifest file.
So the problem was, my App was not authorized to add appointments to User's calendar which is why the appointment Id was returning empty (Would have been nice if the relevant error was returned too, Microsoft).
To fix this, add the following line to your package.appxmanifest file in Capabilities:
<Capabilities>
<uap:Capability Name="appointments" />
</Capabilities>
Or alternatively, you can just click on the file, go to Capabilities tab and check the "Appointments" capability like in the screenshot below:
I'm using Microsoft.Office.Interop.Outlook to load appointments into the interface and to edit appointments . I can load appointments completely fine, editing does work but not 100% of the time. Sometimes I get the error The operation cannot be performed because the message has been changed
May I ask is there a better approach for editing appointments to avoid The operation cannot be performed because the message has been changed error. Error occurs when trying to Save()
public Outlook.AppointmentItem EditOutlookAppointment(CustomAppointment appointment, int retries = 0)
{
Outlook.AppointmentItem appointReturned = null;
try
{
Outlook.Application outlookApp = new Outlook.Application();
MAPIFolder calendarFolder = outlookApp.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderCalendar);
for (int i = calendarFolder.Items.Count; i > 0; i--)
{
var appointmentItem = ((Outlook.AppointmentItem)calendarFolder.Items[i]);
if (appointmentItem.GlobalAppointmentID == appointment.UniqueId)
{
// set some properties
appointmentItem.Subject = appointment.Subject;
appointmentItem.Body = appointment.Body;
appointmentItem.Location = appointment.Location; //Set the location
appointmentItem.Start = appointment.Start;
appointmentItem.End = appointment.End;
appointmentItem.Save();
return appointmentItem;
}
}
}
catch (Exception ex)
{
//Error message implementation here
}
return appointReturned;
}
Firstly, errors like that are unavoidable - it means the appointment was modified between the time Outlook opened it and the time you called Save. And since Outlook really likes to cache the appointments (it always caches the appointment being edited in Outlook or the previously edited appointment), that period of time can be quite large. This can happen if the appointment was modified by the Exchange server itself or by an incoming meeting update processing.
Secondly, looping through all items in the Calendar folder can be a huge performance problem. Unfortunately Outlook won't let you search (Items.Find/FindNext and Items.Restrict) on binary (PT_BINARY) properties such as GlobalAppointmentID. You'd need to use Extended MAPI (C++ or Delphi) or Redemption (I am its author - any language) for this: (RDOtems.Find/FindNext/Restrict in Redemption allow to search on binary properties).
UPDATE. The following should work using Redemption (off the top of my head):
publicRedemption.RDOAppointmentItem EditOutlookAppointment(CustomAppointment appointment, int retries = 0)
{
try
{
Outlook.Application outlookApp = new Outlook.Application();
Redemption.RDOSession session = new Redemption.RDOSession();
session.MAPIOBJECT = outlookApp.Session.MAPIOBJECT; //share the Outlook session
RDOFolder calendarFolder = session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderCalendar);
Redemption.RDOAppointmentItem appointmentItem = calendarFolder.Items.Find("GlobalAppointmentID = '"+appointment.UniqueId + "'");
if (appointmentItem != null)
{
// set some properties
appointmentItem.Subject = appointment.Subject;
appointmentItem.Body = appointment.Body;
appointmentItem.Location = appointment.Location; //Set the location
appointmentItem.Start = appointment.Start;
appointmentItem.End = appointment.End;
appointmentItem.Save();
return appointmentItem;
}
}
catch (Exception ex)
{
//Error message implementation here
}
return null;
}
I am trying to get a list of all the appointments for the day on a shared calendar. I have successfully done it for my own calendar tied to my user account. I tried getting the folderId of the shared calendar, but I haven't been able to find it.
I used this to access my calendar using its folderId and it worked:
Console.WriteLine("Listing appointments...");
//open the calendar
CalendarFolder calendar = CalendarFolder.Bind(service, WellKnownFolderName.Calendar);
//query for appointments in next 10 days
//FindItemsResults<Appointment> appointments = calendar.FindAppointments(new CalendarView(DateTime.Now, DateTime.Now.AddDays(10)));
//find appointments and write out subject
foreach (Appointment appointment in service.FindItems(new FolderId("FOLDERIDHERE"), new ItemView(int.MaxValue)))
Console.WriteLine(appointment.Subject);
I don't know if this will work to access a shared folder, and I can't figure out the folderID of the shared folder.
You could try something like this :
CalendarModule calModule = (CalendarModule)this.Application.ActiveExplorer().NavigationPane.Modules.GetNavigationModule(OlNavigationModuleType.olModuleCalendar);
foreach (NavigationGroup group in calModule.NavigationGroups)
{
NavigationFolders folders = group.NavigationFolders;
MAPIFolder OtherFolder = null;
Items OtherFolderItems = null;
for (int i = 1; i <= group.NavigationFolders.Count; i++)
{
OtherFolder = folders[i].Folder; //This does not work for me
OtherFolderItems = OtherFolder.Items;
}
Unfortunately, this code did not work for me, as trying to access the .Folder launch an exception. I found this on this link : http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/4891d3a5-f578-495c-83aa-f5a914474c78/
I would like to add a recurring event with C#. I found on the Web that the following should work. When I run the method to insert the entry, It fails on the
EventEntry insertedEntry = service.Insert(calendarUri, entry); statement !
I get this error :
"Execution of request failed:
https://www.google.com/calendar/feeds/user#gmail.com/private/full?gsessionid=6eGsOTuhQ-YUVWp2BV_25g"
When I remove the recurrence code, everything works fine ! I noticed that this piece of code is pretty old ! How can I simply add a recurring event on Google Calendar with the .NET library ?
EventEntry entry = new EventEntry();
entry.Title.Text = "Hello World !";
// Recurring event:
String recurData =
"RRULE:FREQ=WEEKLY;WKST=SU;UNTIL=20131010;BYDAY=SU\r\n";
Recurrence recurrence = new Recurrence();
recurrence.Value = recurData;
entry.Recurrence = recurrence;
string htmlDescription = "Woww, really ?";
if (htmlDescription != null && htmlDescription.Length > 0)
{
entry.Content.Type = "html";
entry.Content.Content = htmlDescription;
}
Where eventLocation = new Where();
eventLocation.ValueString = "Somewhere";
entry.Locations.Add(eventLocation);
DateTime start = DateTime.Now;
When eventTime = new When();
eventTime.StartTime = start;
DateTime endTime = DateTime.Now.AddHours(2);
eventTime.EndTime = endTime;
entry.Times.Add(eventTime);
eventTime.AllDay = true;
EventEntry insertedEntry = service.Insert(calendarUri, entry);
Straight from Google (click the .NET example if it doens't come up as a default):Create Recurring Events
Hopefully this will give you some ideas if not out-right answer your question.
Cheers.
Your recurrence string telling it when to end requires a full time entry. You simply said UNTIL=20131010. The question is 20131010 where? We can assume you want midnight, but then... midnight where?
String recurData =
"RRULE:FREQ=WEEKLY;WKST=SU;UNTIL=20131010T000000-05:00;BYDAY=SU\r\n";
The above change should make your event recur until Midnight US Eastern time on 2013-10-10.
I am building an app that is required to live in a trusted domain, monitor a collection of mailbox-calendars on an exchange server in that domain, and sync appointments to different mailboxes on one or many other servers. The mailbox it is synced with is defined in an internal mapping table (sqlce) that is maintained by the user of this application.
The problem I have is I can not work out a way to keep track of the remote appointment so that I can update or delete it if necessary. After I create the appointments on the remote server they have a new itemid which does not correspond to the one returned by the sync folder items call on the local exchange server. I can't find the item by start time/subject as these may have been changed or deleted.
My sync method is below - am I going about this entirely the wrong way or is there a better way to use the SyncFolderItems method?
The best approach I have come up with so far to get around my problem is to save ItemID of the remote appointment into a property of the local appointment but even this I am not sure will work because I don't know what properties are maintained after a delete? Please Help!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Exchange.WebServices.Data;
using System.Net;
namespace ProExchangeSync2012
{
class ExchangeWebServiceMethods
{
public string ProExchangeSyncCalendars(string LocalMailbox
,string RemoteMailbox
,string SyncState
,ExchangeService RemoteService
,ExchangeService LocalService
)
{
//if SyncState is empty string set to null
if (SyncState.ToString().Length == 0)
{ SyncState = null; }
ExchangeService LocalExchangeService = LocalService;
ExchangeService RemoteExchangeService = RemoteService;
RemoteExchangeService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress,RemoteMailbox);
//Folders and mailboxes to pass to the webservice in SyncItems call.
Mailbox DonorMailBox = new Mailbox(LocalMailbox);
Mailbox DestinationMailBox = new Mailbox(RemoteMailbox);
FolderId DonorFolder = new FolderId(WellKnownFolderName.Calendar, DonorMailBox);
FolderId DestinationFolder = new FolderId(WellKnownFolderName.Calendar, DestinationMailBox);
//Create a ChangeCollection object and call syncfolderitems on local exchange service.
ChangeCollection<ItemChange> ItemChanges
= LocalExchangeService.SyncFolderItems(new FolderId(WellKnownFolderName.Calendar, DonorMailBox) //PASS IN THE MAILBOX HERE>
, PropertySet.FirstClassProperties
, null
, 512
, SyncFolderItemsScope.NormalItems
, SyncState
);
//Store the SyncState
SyncState = ItemChanges.SyncState;
//Fetch all the required properties of the items
//LocalService.LoadPropertiesForItems(ItemChanges, PropertySet.FirstClassProperties);
if (ItemChanges.Count == 0)
{
Console.WriteLine("There are no items to synchronize.");
}
else
{
foreach (ItemChange ic in ItemChanges)
{
if (ic.ChangeType == ChangeType.Create)
{
Appointment lappointment = Appointment.Bind(LocalExchangeService, ic.ItemId);
Appointment rappointment = new Appointment(RemoteExchangeService);
rappointment.Subject = lappointment.Subject;
rappointment.Start = lappointment.Start;
rappointment.Body = lappointment.Body;
rappointment.End = lappointment.End;
rappointment.Location = lappointment.Location;
rappointment.Save();
}
else if (ic.ChangeType == ChangeType.Update)
{
//Bind to the local appointment and get the start date
Appointment lappointment = Appointment.Bind(LocalExchangeService, ic.ItemId);
DateTime StartDate = lappointment.Start;
ItemId ItemToUpdate = ItemIDSearch(RemoteExchangeService,StartDate,lappointment.Subject);
//Bind to the remote appointment using ItemToUpdate & update all the details
//this is is less intensive than comparing the appointments for changes.
Appointment rappointment = Appointment.Bind(RemoteExchangeService, ItemToUpdate);
rappointment.Subject = lappointment.Subject;
rappointment.Start = lappointment.Start;
rappointment.Body = lappointment.Body;
rappointment.End = lappointment.End;
rappointment.Location = lappointment.Location;
rappointment.Save();
}
else if (ic.ChangeType == ChangeType.Delete)
{
Appointment lappointment = Appointment.Bind(LocalExchangeService, ic.ItemId.UniqueId);
DateTime StartDate = lappointment.Start;
ItemId ItemToUpdate = ItemIDSearch(RemoteExchangeService, StartDate, lappointment.Subject);
Appointment rappointment = Appointment.Bind(RemoteExchangeService, ic.ItemId.UniqueId);
rappointment.Delete(DeleteMode.MoveToDeletedItems);
}
}
}
return SyncState;
}
//End of Sync Method
//Below method returns a single itemid from exchange service based on start datetime of an appointment in a mailbox.
public ItemId ItemIDSearch(ExchangeService ExchangeService, DateTime AppointmentStart, string subject)
{
ItemId FoundItem;
ItemView iv = new ItemView(1000);
iv.Traversal = ItemTraversal.Associated;
SearchFilter.SearchFilterCollection searchFilterCollection = new SearchFilter.SearchFilterCollection(LogicalOperator.And);
searchFilterCollection.Add(new SearchFilter.IsEqualTo(AppointmentSchema.Subject,subject));
searchFilterCollection.Add(new SearchFilter.IsEqualTo(AppointmentSchema.Start, AppointmentStart));
FindItemsResults<Item> fiitems = ExchangeService.FindItems(WellKnownFolderName.Calendar, searchFilterCollection, iv);
if (fiitems.Items.Count == 1)//if we only get one result do the work else return null
{
FoundItem = fiitems.Items[0].Id;
}
FoundItem = null;
return FoundItem;
}
}
}
So the final solution to all this, which came from the messiah of Exchange Web Services, Glen Scales, was that I stored the "CleanGlobalObjectId" of the appointments I was synching with in my internal database along with the EWS UniqueId that is returned when calling the EWS SyncFolderItems method.
Using the CleanGlobalObjectId which is an extended property of the appointment, I was always able to find a specific appointment on the server even if it had been hard deleted, because the value of this property never changes.