Getting to UM voicemail greetings for users in o365 using PowerShell or EWS - powershell

Hopefully a simple question - at one time I know when user's recorded their personal greetings for UM voicemail in o365 (regular greeting and/or extended absence greeting) these were stored in their Exchange inbox using a special item type (i.e. "IPM.Configuration.Um.CustomGreetings.External"). However setting up my test o365 setup, getting UM configured and all that, after recording my personal greeting and going through each item starting from the root of my inbox, (some 900+ items - lots of odd stuff in there) - I don't see anything like this any more. Lots of log, activity items, some messages but nothing about greetings. Extracting everything that could cast to an email type to a folder I went through each one - nothing promising.
anyone have any clues where the custom greetings for users UM (not auto attendant recordings - that's a different beast) has gone off to and how to get to it?
thanks much.

Got this working after a bit of flailing.
Ben Lye's post that Glen Scales provided in the comments above was what got me from A to B - Thanks Glen.
http://www.onesimplescript.com/2015/07/getting-um-voicemail-greetings-in.html
In related news Glen's very excellent PowerShell plug in for FAI exploration was also very helpful and can save you a bunch of time ramping up on the Folder Associated Information:
https://gsexdev.blogspot.com/2018/03/ews-fai-module-for-browsing-and.html
For those stumbling around with this in .NET using EWS, here's a quick stripped down blurb of code for fetching the standard and extended greeting recordings for a user - took me longer to slog through this than it should have, perhaps this can save someone a bit of time down the road:
note to run this against multiple mailboxes you'll need to have configured impersonation for your account you're authenticating with for EWS functions.
ExchangeService _service;
_service = new ExchangeService(ExchangeVersion.Exchange2016); // Exchange2013_SP1);
_service.Credentials = new WebCredentials("user#domain", "myPw");
_service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
//select the user you're fetching greetings for
_service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "user#domain");
//get the root folder for the current account
var oParamList = new List<FolderId> {WellKnownFolderName.Root};
var oTemp = _service.BindToFolders(oParamList, PropertySet.FirstClassProperties);
var oRoot = oTemp.First().Folder;
var oView = new ItemView(50)
{
PropertySet = new PropertySet(BasePropertySet.FirstClassProperties),
Traversal = ItemTraversal.Associated
};
SearchFilter oGreetingFilter = new SearchFilter.ContainsSubstring(ItemSchema.ItemClass,
"IPM.Configuration.Um.CustomGreetings", ContainmentMode.Substring, ComparisonMode.IgnoreCase);
var oResults = _service.FindItems(oRoot.Id, oGreetingFilter, oView);
//fetch the binary for the greetings as values
var oPropSet = new PropertySet(BasePropertySet.FirstClassProperties);
var oRoamingBinary = new ExtendedPropertyDefinition(31753, MapiPropertyType.Binary);
oPropSet.Add(oRoamingBinary);
_service.LoadPropertiesForItems(oResults, oPropSet);
var strFileName = "";
foreach (var oItem in oResults.Items)
{
if (oItem.ItemClass.Equals("IPM.Configuration.Um.CustomGreetings.External",
StringComparison.InvariantCultureIgnoreCase))
strFileName = "jlindborg_Standard.wav";
if (oItem.ItemClass.Equals("IPM.Configuration.Um.CustomGreetings.Oof",
StringComparison.InvariantCultureIgnoreCase))
strFileName = "jlindborg_Extended.wav";
File.WriteAllBytes("d:\\" + strFileName, (byte[]) oItem.ExtendedProperties.First().Value);
}
}

Related

send email from currently logged in user when checkbox is TRUE

My question is an extension of this fantastic solution but am hoping to take it one step further. Whenever a user other than me marks the checkbox as TRUE, the sender of the email is always me, since:
Installable triggers always run under the account of the person who created them
Is it possible to capture the user currently logged in and make them the sender, and if so, what am I missing in my code to make that happen?
What I've tried
I believed I had found my answer, but no such luck. This still posts the timestamp to the sheet successfully, but the email sender still shows as me. Any help would be greatly appreciated.
Edit(s):
I noticed in the above solution if (activeUser === effectiveUser) { only "worked" if typed as if (activeUser !== effectiveUser) {. In my attempts at making it work, I made that edit and forgot to revert it.
function sendEmail(e){
var sheet = e.source.getActiveSheet();
var cell = e.range;
var activeUser = Session.getActiveUser().getEmail();
var effectiveUser = Session.getEffectiveUser().getEmail();
if (activeUser !== effectiveUser) {
Logger.log(cell.getColumn());
Logger.log(cell.isChecked());
//Check if the checkbox in column G(index 7) was checked
if(sheet.getName() == "actionItems" && cell.getColumn() == 7 && cell.isChecked()){
//get current row values from column A to column F
var values = sheet.getRange(cell.getRow(),1,1,6).getDisplayValues().flat();
Logger.log(values);
var transmittalNumber = values[0];
var email = values[5];
var query = values[1];
//create and update the email's hmtl body
var templ = HtmlService.createTemplateFromFile('html_template');
templ.query = query;
var message = templ.evaluate().getContent();
//Send email
MailApp.sendEmail({
to: email,
subject: "Oatsies Action Item: "+transmittalNumber,
htmlBody: message
});
//Add timestamp at column H(index 8)
var timeZone = "GMT-7";
var date = Utilities.formatDate(new Date(),timeZone, "yyyy-MM-dd HH:mm");
sheet.getRange(cell.getRow(),8).setValue(date);
}
}
}
From the question
Is it possible to capture the user currently logged in and make them the sender?
If you are using a free Google account (usually gmail.com account) it might be possible if you use the Gmail API and set a way for active users to authorize the access to their Gmail account to send emails. Also it might be possible if you and the user are using a Google Workspace accounts and if you are able to take advantage of company-wide delegation of authority (also might be possible if you are able to configure the user's email addresses as emails aliases of your account).
Regarding the use of Session.getActiveUser().getEmail() it will return the active user email based on a complex rules i.e. your account and active user belongs to the same Google Workspace domains.
Related
Session.getActiveUser().getEmail() results are inconsistent
Domain-wide delegation
getActiveUser() doesn't return the user?

right way to query calendar items via ews managed api?

I've got the following code:
var startProp = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.Meeting, "DTSTART", MapiPropertyType.String);
var endProp = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.Meeting, "DTEND", MapiPropertyType.String);
var cond1 = new SearchFilter.IsEqualTo(startProp, StartDate);
var cond2 = new SearchFilter.IsEqualTo(endProp, EndDate);
var filter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, cond1, cond2);
var items = svc.FindItems(WellKnownFolderName.Calendar, filter, view);
I am trying to run this query on an exchange room mailbox. (This is not apparent in the code above however). It may have reservation with the exact start/end time. Hence if there is one reservation matching that criteria, I should get at least one item.
The background to this: think of a meeting room and people are trying to block it for a meeting. On exchange, this is just another mailbox, similar to a user mailbox. So on successful reservation, this mailbox gets an email with the calendar details (iCalendar format (*.ics).
I am stuck on two different counts...
items don't return anything in the code above. The TotalCount is zero. Maybe I am doing something wrong with the api. I am unable to figure this.
I am actually confused with what I am trying to query. I don't understand exchange's resolution in this matter. This is described further below.
So you've email items in a room mailbox. Each email has the calendar embedded with it usually with some base64 encoding. The calendar has a specific schema - we are only interested in the data you find in between VEVENTS (i.e BEGIN:VEVENT and END:VEVENT). The issue here is that there can be multiple VEVENTS sometimes. So how does exchange really do it? Does it run through all the VEVENTS, match the criteria; if it matches successfully, does it return that "email" (with the calendar attached/embedded)? Or it is some other mechanism?
Hence I am unsure of the semantic I've written in the code above. So please advise on this.
Found the answer to the first part:
static void Find(DateTime Start, DateTime End, ExchangeService svc)
{
var filter1 = new SearchFilter.IsGreaterThanOrEqualTo(MeetingRequestSchema.Start, Start);
var filter2 = new SearchFilter.IsLessThanOrEqualTo(MeetingRequestSchema.End, End);
var filter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, filter1, filter2);
var vw = new ItemView(99);
var items = svc.FindItems(WellKnownFolderName.Calendar, filter, vw);
Console.WriteLine("Count: {0}", items.TotalCount);
}

SharePoint O365 Get All Users With Certain Role

Is there a way to get all users who have "Full Control" permission at a site collection and subsite level and change their role to something else? PowerShell would be ideal but CSOM would be fine too if that's the only way to do it. I know I can get groups by role but I need a way to get explicitly added users.
I tried this older StackOverflow question: Get all the users based on a specific permission using CSOM in SharePoint 2013 but I keep getting a 403 forbidden error on the first ctx.ExecuteQuery(); for all sites even though I am a collection administrator. I also wonder if anyone had a PowerShell way to do it.
According the error message, I suspect that the credential is missing in your code.
Please try the code below and let me know whether it works on your side.
static void Main(string[] args)
{
var webUrl = "[your_site_url]";
var userEmailAddress = "[your_email_address]";
using (var context = new ClientContext(webUrl))
{
Console.WriteLine("input your password and click enter");
context.Credentials = new SharePointOnlineCredentials(userEmailAddress, GetPasswordFromConsoleInput());
context.Load(context.Web, w => w.Title);
context.ExecuteQuery();
Console.WriteLine("Your site title is: " + context.Web.Title);
//Retrieve site users
var users = context.LoadQuery(context.Web.SiteUsers);
context.ExecuteQuery();
foreach(var user in users)
{
Console.WriteLine(user.Email);
}
}
}
private static SecureString GetPasswordFromConsoleInput()
{
ConsoleKeyInfo info;
SecureString securePassword = new SecureString();
do
{
info = Console.ReadKey(true);
if (info.Key != ConsoleKey.Enter)
{
securePassword.AppendChar(info.KeyChar);
}
}
while (info.Key != ConsoleKey.Enter);
return securePassword;
}
}
You need the SharePoint Online Management shell to stand up a connection to O365 via Powershell.
Introduction to the SharePoint Online management shell

Google Apps - Share Contacts within Org

Within our Google Apps Org, I would like to setup a shared contact list that anyone inside our company can access and add/edit the contacts so we have all the same information. What is the best way to go about this?
I would create an application in App Engine that uses the Google APIs to edit the Shared Contacts list. That way you can restrict access to your domain users and also audit the activity that is occurring. There are third party tools out there that can edit the shared contact list but this is typically locked down to avoid situations where users delete contacts they should not be able to. Don't forget that the Shared Contacts list that appears in Gmail's type-ahead has a 24 hour delay.
Hey for anyone out there we used a Google Sheet, now anyone can update the sheet and you can either set an automated trigger to upload them on a schedule or manually push them into the Google Directory.
gsheet:
First we pull all the contacts from the directory then you can add/update/delete existing or new contacts.
Then push them to the Directory using the menu.
We made setup super simple so it auto grabs the user info and domain etc without the user having to do anything
var SHEET_NAME = 'google';
var ERROR_RECIPIENT_MAIL= Session.getActiveUser().getEmail();
var DOMAIN = ERROR_RECIPIENT_MAIL.replace(/.*#/, "");
Then we call the Domain Shared Contacts API to get all the data and put it into an array:
function getAllContacts(){
var contacts = ContactsApp.getContacts();
var lastRow = SpreadsheetApp.getActiveSpreadsheet().
getSheetByName(SHEET_NAME).getLastRow();
if (lastRow >2) SpreadsheetApp.getActiveSpreadsheet().
getSheetByName(SHEET_NAME).deleteRows(3, lastRow*1-2);
var contacts = ContactsApp.getContacts();
var params = {
method : "get",
contentType : "application/atom+xml",
headers : {"Authorization": "Bearer " +
ScriptApp.getOAuthToken(),"GData-Version": "3.0"},
muteHttpExceptions : true
};
var startIndex=1;
var data,respCode,resp;
resp = UrlFetchApp.fetch("//www.google.com/m8/feeds/contacts/"
+DOMAIN+"/full?alt=json&start-index="+startIndex, params);
respCode=resp.getResponseCode();
//SpreadsheetApp.getActiveSpreadsheet().
// getSheetByName(SHEET_NAME).getRange("A10").setValue(resp);
data = JSON.parse(resp.getContentText());
From there it's then put in the correct fields on the sheet for the user to update. Then the user selects the action from the drop down which calls the appropriate function when the script is run to update.
Example of delete function:
function deleteContact(contactID,rowNumber){
var params = {
method : "delete",
contentType : "application/json",
headers : {"Authorization": "Bearer " + ScriptApp.getOAuthToken(),"GData-Version": "3.0","If-Match":"*"}
};
var resp = UrlFetchApp.fetch(contactID, params);
var respCode=resp.getResponseCode();
if (respCode=='201' || respCode=='200') {
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME).deleteRow(rowNumber*1);
}
else{
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME).getRange(rowNumber*1, 15, 1, 1).setValue('ERROR');
ERROR_COUNT=ERROR_COUNT.toString()+rowNumber;
}
}
It's pretty cool, and now we are working on bulk data entry as it seems to stall or stop after about 700 contacts in a single run. Also some of the contacts don't get synced and have an error which we'll work on shortly to get the user more info and even store the missed contact to be fixed and updated after. Anyway hope that helps and gives you some ideas.
Ours is located here for anyone interested.

setting up script to include google docs form data in email notification

I've setup a form using googledocs. I just want to have the actual data entered into the form emailed to me, as opposed to the generic response advising that the form has been completed.
I have no skill or experience with code etc, but was sure i could get this sorted. I've spent hours+hours and haven't had any luck.
My form is really basic.it has 5 fields. 4 of which are just text responses, and one multiple choice.
I found this tute online (http://www.labnol.org/internet/google-docs-email-form/20884/) which i think sums up what i'm trying to do, but have not been able to get it to work.
from this site i entered the following code:
function sendFormByEmail(e)
{
var email = "reports.mckeir#gmail.com";
var subject = "Google Docs Form Submitted";
var s = SpreadsheetApp.getActiveSheet();
var headers = s.getRange(1,1,1,s.getLastColumn()).getValues()[0];
var message = "";
for(var i in headers)
message += headers[i] + ' = '+ e.namedValues[headers[i]].toString() + "\n\n";
MailApp.sendEmail(email, subject, message);
}
To this, i get the following response: ->
Your script, Contact Us Form Mailer, has recently failed to finish successfully. A summary of the failure(s) is shown below. To configure the triggers for this script, or change your setting for receiving future failure notifications, click here.
The script is used by the document 100% Club.
Details:
Start Function Error Message Trigger End
12/3/12 11:06 PM sendFormByEmail TypeError: Cannot call method "toString" of undefined. (line 12) formSubmit 12/3/12 11:06 PM
Is anyone able to help shed some light on this for me? I'm guessing i'm not including some data neeeded, but i honestly have no clue.
Workaround http://www.labnol.org/internet/google-docs-email-form/20884/
You have to setup app script to forward the data as email.
I'll point to the comment above that solved it for me: https://stackoverflow.com/a/14576983/134335
I took that post a step further:
I removed the normal notification. The app script makes that generic text redundant and useless now
I modified the script to actually parse the results and build the response accordingly.
function sendFormByEmail(e)
{
var toEmail = "changeme";
var name = "";
var email = "";
// Optional but change the following variable
// to have a custom subject for Google Docs emails
var subject = "Google Docs Form Submitted";
var message = "";
// The variable e holds all the form values in an array.
// Loop through the array and append values to the body.
var s = SpreadsheetApp.getActiveSheet();
var headers = s.getRange(1,1,1,s.getLastColumn()).getValues()[0];
// Credit to Henrique Abreu for fixing the sort order
for(var i in headers) {
if (headers[i] = "Name") {
name = e.namedValues[headers[i]].toString();
}
if (headers[i] = "Email") {
email = e.namedValues[headers[i]].toString();
}
if (headers[i] = "Subject") {
subject = e.namedValues[headers[i]].toString();
}
if (headers[i] = "Message") {
message = e.namedValues[headers[i]].toString();
}
}
// See https://developers.google.com/apps-script/reference/mail/mail-app#sendEmail(String,String,String,Object)
var mailOptions = {
name: name,
replyTo: email,
};
// This is the MailApp service of Google Apps Script
// that sends the email. You can also use GmailApp here.
MailApp.sendEmail(toEmail, subject, message, mailOptions);
// Watch the following video for details
// http://youtu.be/z6klwUxRwQI
// By Amit Agarwal - www.labnol.org
}
The script utilized in the example is extremely generic but very resilient to change because the message is built as a key/value pair of the form fields submitted.
If you use my script you'll have to tweak the for loop if statements to match your fields verbatim. You'll also want to edit the toEmail variable.
Thanks again for the question and answers. I was about to ditch Google Forms as the generic response was never enough for what I was trying to do.
Lastly, in response to the actual problem above "toString of undefined" specifically means one of the form fields was submitted as blank. If I had to guess, I would say the author only used this for forms where all the fields were required or a quick undefined check would've been put in place.
Something like the following would work:
for(var i in headers) {
var formValue = e.namedValues[headers[i]];
var formValueText = "";
if (typeof(formValue) != "undefined") {
formValueText = formValue.toString();
}
message += headers[i] + ' = '+ formvalueText + "\n\n";
}
I haven't tested this precisely but it's a pretty standard way of making sure the object is defined before trying methods like toString() that clearly won't work.
This would also explain Jon Fila's answer. The script blindly assumes all of the header rows in the response are sent by the form. If any of the fields aren't required or the spreadsheet has fields that are no longer in the form, you'll get a lot of undefined objects.
The script could've been coded better but I won't fault the author as it was clearly meant to be a proof of concept only. The fact that they mention the replyTo correction but don't give any examples on implementing it made it perfectly clear.
If this is a Google Form, do you have any extra columns in your spreadsheet that are not on the form? If you delete those extra columns then it started working for me.
You don't need to use a script. Simply go to Tools >> Notification Rules on your Google Spreadsheet. There you can change the settings to receive an email with your desired information every time the document is changed.