Permissions vs. EffectiveRights - exchangewebservices

I am using the calendar.Permissions to find out which rights the logged-in person has on another.
var myEmail = GetPrimaryMailForIdentity(User.Identity);
...
service.Credentials = new WebCredentials(serviceAccountName, serviceAccountPass);
service.AutodiscoverUrl(toEmail);
...
var calendar = CalendarFolder.Bind(service,WellKnownFolderName.Calendar)
foreach (var permission in calendar.Permissions) {
if(permission.UserId.PrimarySmtpAddress == myEmail) {
if(permission.ReadItems == FolderPermissionReadAccess.TimeOnly) permission = "TimeOnly";
...
}
}
This only gives me some of the various rights one person can have on another mailbox, since there could also be a FullAccess rule, or the mailbox could be that of a resource he owns/manages. So I could also impersonate the user for whom to get the permission, and check the effective rights:
var myEmail = GetPrimaryMailForIdentity(User.Identity);
...
service.Credentials = new WebCredentials(serviceAccountName, serviceAccountPass);
service.AutodiscoverUrl(toEmail);
service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, myEmail);
...
var calendar = CalendarFolder.Bind(service,WellKnownFolderName.Calendar)
var permissions = calendar.EffectiveRights;
if(permissions.HasFlag(EffectiveRights.Read) {
permission = "TimeOnly"; // TODO
}
But how can I find out WHAT he is allowed to read - all details, or only time?

The FolderPermissionReadAccess enumeration seems to be what you're looking for. EffectiveRights doesn't expose that level of information.

Related

Polling a Google Contacts change using an apps script

I need to detect change in my contacts book, unfortunately People API provides no such feature. I have read in some blogs polling is needed. I dont find any API to poll https://developers.google.com/contacts/v3/reference Can anyone suggest me how can this feature be achieved?
You can use this sort of function to receive and email if either the number of your contacts or the number of your contact emails change.
function sendEmailIfContactChange() {
const gObj = PropertiesService.getScriptProperties().getProperties();
const contacts=ContactsApp.getContacts();
var n=0;
contacts.forEach((c)=>{c.getEmails().forEach((e)=>{n++;});});
if(!gObj.hasOwnProperty('contacts')) {
gObj.contacts=contacts.length;
gObj.emails=n;
PropertiesService.getScriptProperties().setProperties(gObj);
return;
} else {
let pc = gObj.contacts;
let pe = gObj.emails;
gObj.contacts = contacts.length;
gObj.emails = n;
PropertiesService.getScriptProperties().setProperties(gObj);
if(pc != contacts.length || pe != n) {
GmailApp.sendEmail(gobj.globals.privateemail,'Contacts Have Change',`Old Contact: ${pc} New Contacts: ${contacts.length} \nOld Emails: ${pe} New Emails: ${n}`);
}
}
}
And you can create polling trigger like this:
function createPollingTrigger() {
const ts = ScriptApp.getProjectTriggers().map(t=>t.getHandlerFunction());
if(!~ts.indexOf('sendEmaiIfContactChange')) {
ScriptApp.newTrigger('sendEmailIfContactChange').timeBased().everyHours(2).create();
}
}

How to update Google Group Settings with Google Apps Script

I have a Google Apps Script which has all the permissions it needs, with the Group Settings API on and working, but does not change certain things. There are no errors given, but the only thing that changes is the name, and the rest does nothing. This is the script:
function modgroup() {
var groupKey = 'finaltest#school.edu.mx';
var resource = {
name: "finalfour",
whoCanContactOwner: "ALL_MEMBERS_CAN_CONTACT",
whoCanJoin: "INVITED_CAN_JOIN",
whoCanViewMembership: "ALL_MEMBERS_CAN_VIEW",
whoCanViewGroup: "ALL_MEMBERS_CAN_VIEW",
whoCanInvite: "ALL_MANAGERS_CAN_INVITE",
whoCanAdd: "ALL_MANAGERS_CAN_ADD",
allowExternalMembers: false,
whoCanPostMessage: "ALL_MEMBERS_CAN_POST",
allowWebPosting: false
}
AdminDirectory.Groups.update(resource, groupKey);
}
Ok, after a bit of investigation and experimentation, I found that there was another API and another format that had to be used so that it will work. You need to activate the Groups Settings API (not the Admin Directory API) and you can see the documentation here.
The format is the following:
function editGroup(){
var groupId = 'finaltest#school.edu.mx';
var group = AdminGroupsSettings.newGroups();
group.name = 'NAME';
group.description = 'DESCRIPTION';
group.whoCanAdd = 'NONE_CAN_ADD';
group.whoCanJoin = 'INVITED_CAN_JOIN';
group.whoCanViewMembership = 'ALL_MEMBERS_CAN_VIEW';
group.whoCanViewGroup = 'ALL_MEMBERS_CAN_VIEW';
group.whoCanInvite = 'ALL_MANAGERS_CAN_INVITE';
group.allowExternalMembers = false;
group.whoCanPostMessage = 'ALL_MEMBERS_CAN_POST';
group.allowWebPosting = true;
group.showInGroupDirectory = false;
group.allowGoogleCommunication = false;
group.membersCanPostAsTheGroup = false;
group.includeInGlobalAddressList = false;
group.whoCanLeaveGroup = 'NONE_CAN_LEAVE';
AdminGroupsSettings.Groups.patch(group, groupId);
}

How to get a Flag property from email message?

Can I somehow get a Flag property from EmailMessage or Item object? There is no getFlag() method, I also didn't find it in item.getPropertyBag(). I'm using ews-java-api-2.0. flag setting on outlook web app
There is a strongly type flag property in EWS in 2013 and greater so you could modify the EWS Java source to cater for that. Otherwise if you use the underlying Extended properties you can get the same information eg
ExtendedPropertyDefinition PR_FLAG_STATUS = new ExtendedPropertyDefinition(0x1090, MapiPropertyType.Integer);
ExtendedPropertyDefinition FlagRequest = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.Common, 0x8530, MapiPropertyType.String);
PropertySet fiFindItemPropset = new PropertySet(BasePropertySet.FirstClassProperties);
fiFindItemPropset.Add(FlagRequest);
fiFindItemPropset.Add(PR_FLAG_STATUS);
FolderId FolderToAccess = new FolderId(WellKnownFolderName.Inbox, MailboxToAccess);
ItemView ivItemView = new ItemView(1000);
ivItemView.PropertySet = fiFindItemPropset;
FindItemsResults<Item> FindItemResults = null;
do
{
FindItemResults = service.FindItems(FolderToAccess, ivItemView);
foreach (Item itItem in FindItemResults.Items)
{
Console.WriteLine(itItem.Subject);
Object FlagValue = null;
if (itItem.TryGetProperty(FlagRequest, out FlagValue))
{
Console.WriteLine("Flag : " + FlagValue);
}
Object PR_FLAG_STATUS_Value = null;
if (itItem.TryGetProperty(PR_FLAG_STATUS, out PR_FLAG_STATUS_Value))
{
Console.WriteLine("PR_FLAG_STATUS : " + PR_FLAG_STATUS_Value);
}
}
ivItemView.Offset += FindItemResults.Items.Count;
} while (FindItemResults.MoreAvailable);
Theres a full list of flag properties https://msdn.microsoft.com/en-us/library/ee201258(v=exchg.80).aspx

Trigger UiApp-builder callback within another routine

Serge's solution here seemed like the way to go about this, but I'm a bit afraid that my circumstances may be too different...
I have a button where users can add a new set of rows with controls to a FlexTable, in order to allow them to insert a new member into a record set. After designing and building the app to do this (and despite assurances to the contrary), a requirement was then added for the users to be able to edit the record sets at a later date.
I've finally managed to get the data retrieved and correctly displayed on the Ui - for single member record sets. As a final stage, I am now attempting to extend this to accommodate record sets having more than one member. Obviously this requires determining how many members there are in the record set, and then adding the new rows/control group to the FlexTable, before loading the member into each control group.
So within this routine, (depending on how many members there are) I may need to trigger the same callback, which the user normally does with a button. However, the difference with Serge's fine example, is that his code triggers the checkbox callback at the end of his routine once all the Ui components are in place. My situation needs to do this on the fly - and so far I'm getting 'Unexpected error', which suggests to me that the Ui is not able to update with the added FlexTable controls before my code attempts to assign values to them.
Does anyone have any insight into this problem? Is my only recourse to completely re-build a fixed Ui and dispense with the dynamic rowset model?
Code follows -
1. event for adding controls:
var app = UiApp.getActiveApplication();
var oFlexGrid = app.getElementById('ExpenseDetail');
var oRowCount = app.getElementById('rowCount');
var oScriptDBId = app.getElementById('scriptDBId');
var iRows = parseInt(e.parameter.rowCount);
var sVId = e.parameter.scriptDBId;
var vGridDefs = loadArrayById(sVId); //retrieve upload definition array from ScriptDB
var vControlNames = [];
if (isOdd(iRows)){
var sColour = 'AliceBlue';
} else {
var sColour = 'LavenderBlush';
};
oFlexGrid.insertRow(0);
oFlexGrid.insertRow(0);
oFlexGrid.insertRow(0);
oFlexGrid.insertRow(0);
oFlexGrid.setRowStyleAttributes(0,{'backgroundColor':sColour});
oFlexGrid.setRowStyleAttributes(1,{'backgroundColor':sColour});
oFlexGrid.setRowStyleAttributes(2,{'backgroundColor':sColour});
oFlexGrid.setRowStyleAttributes(3,{'backgroundColor':sColour});
var vExpenseDef = Get_NamedRangeValues_(CONST_SSKEY_APP,'UIAPP_GridExpense');
iRows = iRows+1;
vControlNames = CreateGrid_MixedSet_(iRows, vExpenseDef, oFlexGrid, app);
oRowCount.setText(iRows.toString()).setValue(iRows.toString());
//SOME INCONSEQUENTIAL CODE REMOVED HERE, LET ME KNOW IF YOU NEED IT
vGridDefs = vGridDefs.concat(vControlNames); // unify grid definition arrays
var sAryId = saveArray('expenseFieldDef', vGridDefs);
oScriptDBId.setText(sAryId).setValue(sAryId); //store array and save ScriptDB ID
if (e.parameter.source == 'btnExpenseAdd'){
hideDialog(); //IGNORE CHEKCBOX-DRIVEN CALLS
};
return app;
2. routine that calls the event
var app = UiApp.getActiveApplication();
var oPanelExpense = app.getElementById('mainPanelExpense');
var oPanelIncome = app.getElementById('mainPanelIncome');
var oPanelEdit = app.getElementById('mainPanelEdit');
var chkExpenseAdd= app.getElementById('chkExpenseAdd');
var bExpenseTrigger = e.parameter.chkExpenseAdd;
var sVoucherId = nnGenericFuncLib.cacheLoadObject(CACHE_EDIT_VOUCHERID);
var sVoucher = e.parameter.ListSearch1Vouchers;
var aryVoucherInfo = getVoucherEditDetail(sVoucherId);
//SAVE FOR RECORD MARKING CALLBACK
nnGenericFuncLib.cacheSaveObject(CACHE_EDIT_OLDRECORDS, JSON.stringify(aryVoucherInfo), CACHE_TIMEOUT);
sVoucher = nnGenericFuncLib.textPad(sVoucher, '0', 7);
var bExp = (sVoucher.substring(0,2) == '03')
var oRowCount = app.getElementById('rowCount');
var iRowCount = parseInt(e.parameter.rowCount);
var sControlName = '';
var vControlVal = '';
var iExpIdx = 0;
var sControlType = '';
var oControl = '';
var vSummaryTotal = 0;
for (var iVal in aryVoucherInfo){
sControlName = aryVoucherInfo[iVal][2];
vControlVal = aryVoucherInfo[iVal][3];
switch (sControlName){
case 'ESUM60':
vSummaryTotal = vControlVal;
break;
case 'EXUSRN':
continue; //DON'T OVERWRITE CURRENT USERNAME
break;
};
if (sControlName.indexOf('_')!=-1){ //TEST FOR CONTROL SET MEMBER
var aryControlSet = sControlName.split('_');
if (parseInt(aryControlSet[1])>iRowCount){//*** TRIGGER THE EVENT ***
Logger.log(bExpenseTrigger + ' - ' + !bExpenseTrigger);
chkExpenseAdd.setValue(!bExpenseTrigger, true);
iRowCount = iRowCount +1;
};
};
oControl = app.getElementById(sControlName);
var vCache = cacheSaveReturn(CACHE_UIEX_LISTS,sControlName);
if (typeof vCache == 'undefined'){
oControl.setValue(vControlVal);
oControl.setText(vControlVal);
//controlSetTextBox(oControl,vControlVal);
//controlSetDateBox(oControl,vControlVal);
} else {
if (!(nnGenericFuncLib.arrayIsReal(vCache))){
vCache = JSON.parse(vCache);
};
vCache = vCache.indexOf(vControlVal);
if (vCache != -1){
oControl.setSelectedIndex(vCache);
} else {
controlSetListBox(oControl,vControlVal);
};
};
};
//SOME CODE REMOVED HERE
hideDialog();
return app;
Mogsdad to the rescue!
The answer (see above) for those at the back of the class (with me) is to simply pass the app instance parameter (e) to the event function, calling it directly from the main routine, thus keeping the chronology in step for when it returns the app to complete the routine. No need for the checkbox in this situation.
This only took me all day, but thanks Mogsdad! :)
Snippet below taken from 1/2 way down code sample 2 in the OP:
if (sControlName.indexOf('_')!=-1){ //TEST FOR CONTROL SET MEMBER
var aryControlSet = sControlName.split('_');
if (parseInt(aryControlSet[1])>iRowCount){
eventAddExpense(e); //THAT'S ALL IT TAKES
iRowCount = iRowCount +1;
};
};

EWS - Attachment Not Sent With Invitation

I am facing an issue with sending attachments with invitation using EWS Managed API. Appointments attendees are not receiving any attachments added to the appointment but
attachment do appears in the calendar of the person that created the appointment.
Here is my code snippet:
try
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP1, TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"));
service.Credentials = new WebCredentials("calendar_user", "password1", "acme");
service.Url = new Uri("https://acme.com/EWS/Exchange.asmx");
Appointment appointment = new Appointment(service);
service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "tin.tin#acme.com");
String UID = "D09F3FF6-1461-414C-89E8-C05BC3B66A4A";
appointment.ICalUid = UID;
appointment.Subject = "Test Subject";
appointment.Body = "Test Content.";
appointment.Start = new DateTime(2012, 07, 11, 17, 00, 0);
appointment.End = appointment.Start.AddMinutes(30);
FileAttachment attachment = appointment.Attachments.AddFileAttachment(#"C:\Users\tintin\Documents\Test.xlsx");
attachment.IsInline = false;
appointment.RequiredAttendees.Add("tin.tin#acme.com");
appointment.Save(SendInvitationsMode.SendToAllAndSaveCopy);
}
catch (Exception ex)
{
}
Look like EWS has horrible limitation with attachment handling. I found a workaround to resolve this issue which requires updating appointment object twice.
appointment.ICalUid = UID;
appointment.Subject = "Test Subject";
appointment.Body = "Test Content.";
appointment.Start = new DateTime(2012, 07, 11, 17, 00, 0);
appointment.End = appointment.Start.AddMinutes(30);
FileAttachment attachment = appointment.Attachments.AddFileAttachment(#"C:\Users\tintin\Documents\Test.xlsx");
attachment.IsInline = false;
appointment.Save(folderCalendar, SendInvitationsMode.SendToNone);
appointment.RequiredAttendees.Add("tin.tin#acme.com");
appointment.Update(ConflictResolutionMode.AutoResolve, SendInvitationsOrCancellationsMode.SendToAllAndSaveCopy);
Looks like this issue is specific to Exchange Server 2010 Service Pack 1. I got similar issue and when I changed the version to SP2 issue got resolved. Below code solved the problem
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
A second update did the trick, but it will cause a cancelled meeting at the bottom. Can't use it in product.
It doesn't work to change the version to SP2.
Still find a better solution.
Yes, EWS has some issue, while updating the meeting with new attachments it is not getting updated for the first time. Needed 2 instances to update it.
Microsoft.Exchange.WebServices.Data.Appointment meet1 = await
Microsoft.Exchange.WebServices.Data.Appointment.Bind(service, strMessageID);
meet1.Attachments.Clear();
foreach (FileUpload Item in
objCreateEvent.strAttachmentUploadPath)
{
meet1.Attachments.AddFileAttachment(Item.fileName,
Item.filePath);
}
meet1.RequiredAttendees.Clear();
foreach (string ToItem in objToIds)
{
meet1.RequiredAttendees.Add(ToItem);
}
await
meet1.Update(ConflictResolutionMode.AlwaysOverwrite,
SendInvitationsOrCancellationsMode.SendToAllAndSaveCopy);
Microsoft.Exchange.WebServices.Data.Appointment
meeting2 = await Microsoft.Exchange.WebServices.Data.Appointment.Bind(service,
strMessageID);
meeting2.Attachments.Clear();
foreach (FileUpload Item in
objCreateEvent.strAttachmentUploadPath)
{
meeting2.Attachments.AddFileAttachment(Item.fileName, Item.filePath);
}
meeting2.RequiredAttendees.Clear();
foreach (string ToItem in objToIds)
{
meeting2.RequiredAttendees.Add(ToItem);
}
await
meeting2.Update(ConflictResolutionMode.AlwaysOverwrite,
SendInvitationsOrCancellationsMode.SendToAllAndSaveCopy);