diff --git a/group-membership/README.md b/group-membership/README.md index 68a244a7..60997912 100644 --- a/group-membership/README.md +++ b/group-membership/README.md @@ -1 +1,39 @@ -# Group Membership +# Give external users access to resources + +*Scale access to external vendors and customers via a Google Sheet and an Apps +Script in G Suite.* + +## Description + +You may manage multiple Google Groups for your organization and wish to streamline this process, especially if you have to manually send an email upon granting users membership. The following Google Spreadsheet contains a script that helps add the email address of a user to a Google Group, and then sends them an email confirming they have been added. + +## Technology highlights + +* Using the `onEdit` *simple trigger* in a *Google Sheet*, you can grant access to multiple resources from a spreadsheet. +* *Google Group permissions* are respected. +* A *Google Document* is used as the email's *template*. + +## Try it + +1. Make a [copy of this Google Spreadsheet](https://docs.google.com/spreadsheets/d/1kNuOc_evfqbu8dVJIA5N4r27d_Ubnr915eln4cbq2cU/copy) from your G Suite account. +1. From your spreadsheet, click on **Tools > Script Editor**. This will bring you to the *Apps Script editor*. +1. Ensure the **Admin Directory API** is enabled via **Resources > Advanced Google Services**. +1. Now run the script by clicking the **"Select function"** drop down > choose **"installTrigger."** Then click the Run button (►). This will create a trigger for your sheet automatically which you can visit on the triggers page by clicking the **trigger icon** (which looks like a clock). + + > *Caution:* If you run this script *more than once*, it will generate *multiple triggers* which would duplicate emails. Ensure you run the script once and that there aren't multiple triggers on the triggers page. +1. When prompted, click the **Review permissions** and click **Allow** so the script can email on your behalf. + + > *Important:* If you get the warning **This app isn't verified**, continue with the verification process by clicking **Advanced** and then scroll down and click the grey text at the bottom that says **Go to (Copy this) external-access to a Google Group via onEdit Sheet** +1. After granting permissions, return to your spreadsheet and *type an email address* in the `Email` column and the *group address* in the `Google Group` column, and the word `yes` in the `Allowed` column. + + > Note if you do not populate the column `allowed` with the word `yes`, the script is instructed to not run. This is helpful if you wish to capture requests but not process them yet until you populate the *allowed* column. + +1. To *test*, enter *your own email* and a *Google Group* you already are a *member* of and have *manager rights* to its membership in order to *receive* the *confirmation* email. + +## Optionally customize your email template + +1. *[optional]* You can modify the font, color, and images of your email template by [making a copy of this doc](https://docs.google.com/document/d/1-ajkkIP8gUWqMcnpXhkqwlM_2Y18USLdJ-pFZdDEZ70/copy). Then copy its unique ID within its URL address (it's a string of text after https://docs.google.com/document/d/ and before `/edit#` which will look like this: `1-ajkkIP8gUWqMcnpXhkqwlM_2Y18USLdJ-pFZdDEZ70`). Then replace the Google Doc ID in the spreadsheet's code, starting on **line 11**. You must set this template's *sharing rights* to **Viewable by anyone with this link** or at least **Viewable by anyone in your organization**. + + > *Caution*: Remember to *not delete* the items in brackets within the *Google Doc* as these are *placeholders* for the script to populate with *custom information* from the sheet such as `{{EMAIL}}` and `{{GOOGLE_GROUP}}`. + +1. [optional] Modify the *subject line* of the confirmation email in the sheet's code on **line 10** by replacing `Added to group` to the text you desire. \ No newline at end of file diff --git a/group-membership/src/group-membership.js b/group-membership/src/group-membership.js index 568f7bc9..1d8c7db0 100644 --- a/group-membership/src/group-membership.js +++ b/group-membership/src/group-membership.js @@ -1,85 +1,93 @@ /* -1) Resources -> Advanced Google Services... - Admin Directory API - Google Sheets API +TODOS TO MAKE YOUR SOLUTION WORK: +1) Enable Admin Directory API (Resources -> Advanced Google Services) 2) Paste your Google Doc IDs below - -TODO:group used myfavoritegroup@googlegroups.com, change body -3) +3) Make Google Doc shareable to anyone with the link (or anyone in + your organization). +4) Run function `installTrigger` to auto create onFormSubmit function */ var addedToGroupSubject = 'Added to group'; -var addedToGroupDocId = '1umU1M67IKdvdf6UpKsGpcxhTJc_78hrlRU7OtXviCtU'; +var addedToGroupDocId = '1-ajkkIP8gUWqMcnpXhkqwlM_2Y18USLdJ-pFZdDEZ70'; -var alreadyInGroupSubject = 'Already in group'; -var alreadyInGroupDocId = '16U9nddvplFHOFLmEawt8Kxzf7Vtplntlty0S0C9mePQ'; +/** + * Must click this function from the top drop-down and click 'run' icon to + * create the onEdit trigger. + */ +function installTrigger() { + ScriptApp.newTrigger('onEdit') + .forSpreadsheet(SpreadsheetApp.getActive()) + .onEdit() + .create(); +} -// Reviews each row upon submission from form. -function onFormSubmit() { +/** + * Sends a customized email when a user is added to a group. + * + * To see more of the onFormSubmit event, see: + * https://developers.google.com/apps-script/guides/triggers/events#edit + */ +function onEdit(e) { + // Get an object from the modified row. var sheet = SpreadsheetApp.getActiveSheet(); - var rows = sheet.getDataRange().getValues(); - var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]; - - // ObjApp library uses the rangeToObjects() method to create a list of - // objects from the spreadsheet's rows. - // Each row is converted into an object, which we will refer to as `user`. - var users = ObjApp.rangeToObjects(rows); - for (var i in users) { - // `user` includes all the fields in a row as an object. - var user = users[i]; + var headers = sheet.getDataRange().offset(0, 0, 1).getValues()[0]; + var row = sheet.getRange(e.range.getRow(), 1, 1, headers.length).getValues(); + var responses = ObjApp.splitRangesToObjects(headers, row)[0]; - // If we make any changes to `user`, we want to keep track that there - // were changes so we can mirror those changes in the spreadsheet. - var rowUpdated = false; + // Get all the response values and store them in local variables. + // ObjApp will create an object with all the fields in camelCase. + var userEmail = responses.email.trim(); + var groupEmail = responses.googleGroup.trim(); + var allowed = responses.allowed.toLowerCase(); - // Check if the group contains the user's email. - var group = GroupsApp.getGroupByEmail(user.googleGroup); - if (group.hasUser(user.email)) { - // User is already in group, send a confirmation email. - var emailBody = docToHtml(alreadyInGroupDocId); - emailBody = emailBody.replace('{{EMAIL}}', user.email); - MailApp.sendEmail({ - to: user.email, - subject: alreadyInGroupSubject, - htmlBody: emailBody, - }); - - // If the membership status is blank, means the user was already added. - if (!user.membershipStatus) { - user.membershipStatus = 'ALREADY ADDED'; - rowUpdated = true; - } - } - else { - // User is not part of the group, add user to group. - var member = {email: user.email, role: 'MEMBER'}; - AdminDirectory.Members.insert(member, user.googleGroup); + // If the user is not allowed, exit from function. + if (allowed != 'yes') + return; - // Send a confirmation email that the member was now added. - var emailBody = docToHtml(addedToGroupDocId); - emailBody = emailBody.replace('{{EMAIL}}', user.email); - MailApp.sendEmail({ - to: user.email, - subject: addedToGroupSubject, - htmlBody: emailBody, - }); - - // Mark the membership status to the current date. - user.membershipStatus = new Date().toString(); - rowUpdated = true; - } + // Check if the group contains the user's email. + var group = GroupsApp.getGroupByEmail(groupEmail); + if (!group.hasUser(userEmail)) { + // User is not part of the group, add user to group. + var member = {email: userEmail, role: 'MEMBER'}; + AdminDirectory.Members.insert(member, groupEmail); - if (rowUpdated) { - // Update the entire row in the sheet with the new `user` values. - var userRow = ObjApp.objectToArray(headers, [user]); - sheet.getRange(i+2, 1, 1, userRow.length).setValues(userRow); - } + // Send a confirmation email that the member was now added. + var emailBody = docToHtml(addedToGroupDocId); + emailBody = emailBody.replace('{{EMAIL}}', userEmail); + emailBody = emailBody.replace('{{GOOGLE_GROUP}}', groupEmail); + MailApp.sendEmail({ + to: userEmail, + subject: addedToGroupSubject, + htmlBody: emailBody, + }); + responses.status = 'Newly added'; } + else { + // User is already in group, do nothing. + responses.status = 'Already in group'; + } + + // Append the status on the spreadsheet to the responses' row. + var sheet = SpreadsheetApp.getActiveSheet(); + row = ObjApp.objectToArray(headers, [responses])[0]; + sheet.getRange(e.range.getRow(), 1, 1, row.length).setValues([row]); + + // Log activity. + Logger.log("responses=" + JSON.stringify(responses)); } -// Fetches a Google Doc as an HTML string for email. +/** + * Fetches a Google Doc as an HTML string. + * + * @param {string} docId - The ID of a Google Doc to fetch content from. + * @return {string} The Google Doc rendered as an HTML string. + */ function docToHtml(docId) { - var url = "https://docs.google.com/feeds/download/documents/export/Export?id="+docId+"&exportFormat=html"; + // This is only used to ask for Drive scope permissions. + // It can be commented out like it is below. + // DriveApp.getStorageUsed(); + var url = "https://docs.google.com/feeds/download/documents/export/Export?id=" + + docId + "&exportFormat=html"; var param = { method: "get", headers: {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},