Exchange Web Services and Property Sets - exchangewebservices

I need to retrieve calendar information by invoking the Exchange Web Service in BPOS. I'm using a CalendarView with a PropertySet to retrieve as little data as possible. However, property sets seems to be limited. I need the EmailAddress of the one who made the calendar appointment so I thought I could use the AppointmentSchema.Organizer in the PropertySet.
When fetching a whole appointment I can get the e-mail through appt.Organizer.EmailAddress. But with the code below the Organizer.EmailAddress is always null. I've enabled the trace and checked it and only the Organizer.Name property is sent, nothing else. Does anyone have a solution on how to get the EmailAddress when using a PropertySet?
CalendarView view = new CalendarView(dtFrom, dtTo);
view.PropertySet = new PropertySet(ItemSchema.Subject);
view.PropertySet.Add(ItemSchema.Id);
view.PropertySet.Add(AppointmentSchema.Start);
view.PropertySet.Add(AppointmentSchema.End);
view.PropertySet.Add(AppointmentSchema.Organizer); // This should contain EmailAddress but it doesn't
Mailbox mailbox = new Mailbox("myemail#test.ab");
FolderId id = new FolderId(WellKnownFolderName.Calendar, mailbox);
CalendarFolder folder = CalendarFolder.Bind(service, id);
FindItemsResults<Appointment> findResults = folder.FindAppointments(view);

This should work (does for me):
service.FindAppointments(WellKnownFolderName.Calendar, new CalendarView(start, end)).Where(s => DateTime.Now < s.Start);
service.LoadPropertiesForItems(appointments, PropertySet.FirstClassProperties);

As best as I have been able to figure out EWS is a little buggy when it comes to populating the full EmailAddress details both in Appointments for Organizer and for other things like "EmailMessage.From". When you do a query for multiple items, you don't get the EmailAddress properties being fully populated. E.g. using APIs like:
Folder.FindItems
ExchangeService.FindAppointments
I find that only the display name in the EmailAddress fields gets populated.
To get the EmailAddress fully populated I find I need to load/bind to the specific item and specify the relevant EmailAddress property, e.g. AppointmentSchema.Organizer in your case. So although you specify exactly the same property to load, you are loading using a single item call rather than a bulk query. E.g. using:
ServiceObject.Load
Which is available for both Appointment and EmailMessage as they both derive off ServiceObject. Using Item.Bind with the appropriate property set defined should also work.
As an aside I figured this out looking at the code for EwsEditor which is mentioned here:
http://blogs.msdn.com/webdav_101/archive/2009/11/10/ews-has-more-happy-now-ews-managed-api-and-ewseditor.aspx
The usability of EwsEditor is fairly sucky, and the code takes some trawling to figure out, but at least it does show examples of exercising many of the APIs.

service.FindAppointments(WellKnownFolderName.Calendar, new CalendarView(start, end)).Where(s => DateTime.Now < s.Start);
service.LoadPropertiesForItems(appointments, PropertySet.FirstClassProperties);
It worked for me.

Related

ContactsApp.getContact() for contact with multiple emails returns a different contact for each email address

My company allows employees to have multiple email addresses in their account.
I am attempting to write an input form where someone can enter any of the emails for a contact and we'll resolve it to the same person.
I've tried doing this via ContactsApp.getContact(email).getPrimaryEmail() to resolve all different inputs to the same primary email, but it doesn't work as expected. Each email I search for returns a different Contact object with only a single email (the one I used to search).
Even if I use ContactsApp.getContact(email).getEmails() to list all emails to the employee, I can see it only returns one at a time.
When going to contacts.google.com, I see the information I expected. Searching for any of the emails will return the same contact, with the same primary email and all other emails listed.
Is there something I'm doing wrong? Or is this how ContactsApp works. If so, is there a workaround?
Thanks!
getPrimaryEmail() will only return an email address if the contact in question has had a default email set and this can only be done via the Google Contacts App, not on the web (go figure).
To solve your problem I would suggest loading all the contacts into an array variable first and then searching this for matching email addresses. I would do the data retrieval on page/view load (so it can be reused without multiple server calls) but have included it all together here for conciseness.
What I found strange about the ContactsApp (and presumable People API too) is that the contacts returned are just empty objects (when logged) with just a bunch of methods on them. However, once you have an array of objects you can write your own properties to those objects for easier access of data.
Therefore I would first of all retrieve all the users contacts, then add a property of .emails to each contact object in the array and then use .some, perhaps, to check if a match appears in .emails (which will also be an array).
Something like:
let strSearch = 'someone#somewhere.net' // EMAIL address to search based on user input
let arrContacts = ContactsApp.getContacts();
let contacts = arrContacts.map(contact => {
let emails = contact.getEmails();
contact.emails = emails.map(email => email.getAddress());
return contact;
});
let foundContact = contacts.filter(contact => contact.emails.some(email => email === strSearch));
Remember .getEmails() returns another array of objects that have the method .getAddress() on them in order to retrieve the actual address, that's why I'm getting an array of email objects with let emails = contact.getEmails(); then using emails.map here to put the actual email addresses into an array stored in the contact.emails property (that doesn't exist so we're actually creating it here).
After that we're filtering the contacts array down to a contact where the searched email address is matched to one of the email addresses in the contact.emails array. I haven't tested it so the last line might need playing around with slightly but I've used something very similar so would expect it to work.
You can then use foundContact[0] to reference the contact found and use the further methods of .getFullname() .getId() etc. to retrieve their data as required. If you need the contacts phone numbers or geographical address that's a whole other process of returning an array of objects using .getPhones() or getAddresses(), but I think that's beyond the scope of this question.

StudentSubmissions.Patch UpdateMask Error

Trying to use the StudentSubmissions.Patch part of the Classroom API in Google Apps Script and keeping running across this error
updateMask: updateMask may only contain "draftGrade" or
"assignedGrade"
Here is my code for that particular section:
var studentSubmission = {'draft_grade':'88'}
var patchC = Classroom.Courses.CourseWork.StudentSubmissions.patch(studentSubmission, courseId, cwId, submissionId);
There is clearly something wrong with the way I am passing the StudentSubmission Resource parameter, but I can't figure out why...
This is clearly the documentation I am referring to - https://developers.google.com/classroom/reference/rest/v1/courses.courseWork.studentSubmissions/patch
UPDATE
I was able to change the code a bit to reflect what you both were saying. Obviously, I didn't use exactly what you both said because KENdi's example is in Python and Ein2012, it would error out on the var patchC = Classroom... line.
I changed some things that now look like this:
var studentSubmission = {'draftGrade':'88'}
var extra = {'updateMask':'draftGrade'};
var patchC = Classroom.Courses.CourseWork.StudentSubmissions.patch(studentSubmission, courseId, cwId, submissionId, extra);
But now I get a different error "#ProjectPermissionDenied The Developer Console project is not permitted to make this request". So, now I'm unsure if that formatting is correct and there is some Developer Console situation I haven't resolved (although feel as though I'm correct), or that new formatting is wrong and I'm just getting the wild permission error.
I saw this Similar Error but what if the course work was one created normally through classroom and not via a script? Ahh.
specify update mask fields and later execute and also specify names as instructed in the documentation ("draftGrade","assignedGrade")
var studentSubmission = {'draftGrade':'88'}
var patchC = Classroom.Courses.CourseWork.StudentSubmissions.patch(studentSubmission, courseId, cwId, submissionId);
patchC.UpdateMask = "draftGrade";
var response = submisionObj.Execute();
From your error, it is stated that the updateMask must only contain "draftGrade" or "assignedGrade". So from your code, you need only that two value for the updateMask.
The updateMask identifies which fields on the student submission to update. This field is required to do an update. The update fails if invalid fields are specified
From this documentation, The StudentSubmission resource has two fields to store grades: assignedGrade, which is the grade reported to students, and draftGrade, which is a tentative grade visible only to teachers. These fields are updated using courses.courseWork.studentSubmissions.patch with a field mask containing the appropriate fields.
Here is the example code on how to do that.
studentSubmission = {
'assignedGrade': 99,
'draftGrade': 80
}
service.courses().courseWork().studentSubmission().patch(
courseId=<course ID or alias>,
courseWorkId=<courseWork ID>,
id=<studentSubmission ID>,
updateMask='assignedGrade,draftGrade',
body=studentSubmission).execute()

EWS SearchFolder does not return values from body

I am trying to create a SearchFolder using the EWS API (managed or web service directly). I noticed that I if I create a SearchFilter.ContainsSubstring on the ItemSchema.Body, I do not get any conversations from it.
here is how I create my folder:
var folder = new SearchFolder(service)
{
DisplayName = topic
};
var searchParameters = folder.SearchParameters;
searchParameters.SearchFilter = new SearchFilter.ContainsSubstring(ItemSchema.Body, topic, ContainmentMode.Substring, ComparisonMode.IgnoreCaseAndNonSpacingCharacters);
searchParameters.RootFolderIds.Add(WellKnownFolderName.Root);
searchParameters.Traversal = SearchFolderTraversal.Deep;
folder.Save(WellKnownFolderName.SearchFolders);
Later, I try to get the conversations from this folder:
service.FindConversation(conversationView, folder.Id);
And this returns 0 conversations.
I made sure by sending two messages to my email account, the first with a special term only in the subject, and the second with the same term in the body. If I create a SearchFolder with a filter on the ItemSchema.Subject, I get the first conversation, but using the SearchFolder I created above, I do not get the expected result.
Are there some restrictions regarding the ContainsSubstring SearchFilter? I tried using NormalizedBody or TextBody, but then I got errors in the folder creation process. Is there anything else I am missing?
Doing a search filter on the body will likely be problematic. This goes back to how potentially large properties like Body are handled in contents tables. A query string search would likely work better, but you can't use a query string to create a search folder.

Confusion with Entity Framework context

I'm a bit confused in regards to how EF's dbContext works.
If I do something like _context.Persons.Add(_person) (assuming person is a valid entity), if I then (before calling _context.SaveChanges()) query Persons, will the person I just added be included in the results?
For example:
Person _person = new Person() {Firstname = "Bill", Lastname = "Snerdly"};
_context.Persons.Add(_person);
var _personList = _context.Persons.Where(p => p.Lastname.StartsWith("Sne"));
Whenever I try this, it seems as though the context loses track of the fact that I've added this new person to the context.
What confuses me is that if I edit an existing person and attach the person and set the state to modified, querying the context seems to keep track of the changes that were made and returns them in the results. For example:
//Assuming that Person 5 exists with the name William Snerdly
Person _person = new Person() {Id = 5, Firstname = "Bill", Lastname = "Snerdly"};
_context.Persons.Attach(_person);
_context.Entry(_person).State = System.Data.EntityState.Modified;
var _personList = _context.Persons.Where(p => p.Lastname.StartsWith("Sne"));
In this case, it seems like the person with the id of 5 will show up in the list with the name Bill instead of William. IOW, the context queried the data but retained the changes while in the first scenario, the context queried the data but ignored any added items. It just seems a bit inconsistant.
Am I understanding this correctly or am I missing something?
Thanks for your help with this.
No, as it does not yet exist in the database. It will, however, be accessible through the ObjectStateManager of the ObjectContext, or alternatively, if you're using the DbContext/DbSet wrappers, through the .Local property of the DbSet.
In the case of the edit, you're seeing the ORM's first level cache at work. The query is executed against the database (and so compares against the values in there - your example would get even weirder if you modified the Lastname in the context, but still get the result from the query looking for the unmodified Lastname), but when its results are processed, first the ID of the returned entity is checked, and since the entity with that ID is already present in the context, you get that instance back. This is the default "AppendOnly" mode of operation.
I don't know what you want to do, but I had to understand all that when I wanted to validate my changes according to rules that needed to use the values of both loaded and unread entities. I ended up starting a transaction, saving the changes with the "None" options, doing my validation queries againt the database (which then contained the "merged" view of the data), and the rolling back the transaction if the data was invalid, or accepting the changes and committing the transaction otherwise.

CRM 4.0 - Is there a way to know if a contact from a specific marketing list has respond to a campaign?

I am wondering how can I do the following about MS CRM 4.0:
I want to know for a campaign if a contact from a specific marketing list has not replied yet.
The field custom in the campaign response form is a partyfield. CRM doesn’t allow a PartyList field to be queried using a QueryExpression
Any ideas?
Thanks,
Katya
You cannot retrieve activityparty records directly, but you can use them in LinkEntities:
private bool contactHasResponded(Guid idCampaign, Guid idContact)
{
QueryExpression qryCampaignResponses = new QueryExpression("campaignresponse");
qryCampaignResponses.ColumnSet = new AllColumns();
qryCampaignResponses.Criteria = new FilterExpression();
qryCampaignResponses.Criteria.AddCondition("regardingobjectid", ConditionOperator.Equal, idCampaign);
LinkEntity leContact = new LinkEntity("campaignresponse", "activityparty", "activityid", "activityid", JoinOperator.Inner);
leContact.LinkCriteria = new FilterExpression();
leContact.LinkCriteria.AddCondition("partyid", ConditionOperator.Equal, idContact);
qryCampaignResponses.LinkEntities.Add(leContact);
List<gcCampaignresponse> lstCampaignResponses = gcCampaignresponse.RetrieveMultiple(m_svcCrm, qryCampaignResponses);
return (lstCampaignResponses.Count > 0);
}
This will tell you whether there's a campaign response for a given campaign and contact. (I use entity classes generated by Stunnware Tools, so the RetrieveMultiple call looks a little different, but I think you get my point).
If you turn this QueryExpression/LinkEntity construct upside down, you can also get all contacts that have responded to a given campaign (you can also restrict that to contacts in a certain marketing list through a second LinkEntity).
The only thing that's not possible directly with a single query is the "negative" check you are looking for, so you'll have to take this result and do an "outer join" against your marketing list to get the contacts that have not responded.