Issue:
- You want to hide or show a sheet in your spreadsheet depending on which user is accessing the spreadsheet.
Solution:
You could do the following:
- Install an onOpen trigger which executes a function (let's call it
fireOnOpen
) every time a user opens the spreadsheet.
- The function
fireOnOpen
should check which user is accessing the spreadsheet, and hide or show a certain sheet (called Internal
) depending on this.
- In order to check the current user accessing the spreadsheet, you can use getActiveUser() (instead of getEffectiveUser(), which will return the user who installed the trigger).
Workflow:
- The trigger can be installed either manually or programmatically. To do it programmatically, copy this function to your script editor and execute it once:
function createOnOpenTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger("fireOnOpen")
.forSpreadsheet(ss)
.onOpen()
.create();
}
- This will result in
fireOnOpen
being executed every time a user accessed the spreadsheet. The fireOnOpen
function could be something like this:
function fireOnOpen() {
const adminUsers = ["email1@gmail.com","email2@gmail.com","email3@gmail.com"];
const currentUser = Session.getActiveUser().getEmail();
const internalSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Internal");
if (adminUsers.includes(currentUser)) internalSheet.showSheet();
else internalSheet.hideSheet();
}
Important notes:
- You cannot hide sheets for some users but not for others. A hidden sheet is hidden for all users, and a visible sheet is visible for all users. Therefore, this will only work if internal and external users don't access the spreadsheet at the same time. If they do, external users might be able to access the
Internal
sheet.
- getActiveUser() is not always populated, as you can see on this answer, so please make sure that all admin users are from the same G Suite domain. Otherwise, this won't work.
- If the privacy of the
Internal
sheet is critical and there is a possibility of internal and external users accessing the spreadsheet at the time, I would not recommend this solution.
Edit:
As mentioned in comments, a possible workaround for the occasions when admin and non-admin users access the file at the time could be the following:
- When an admin user accesses the file, store the time in which that happened.
- Create a time-driven trigger to execute a function periodically (every 5 minutes, let's say), which will check if an admin accessed the file a short time ago (let's say 30 minutes). If the admin has done that, remove the Permissions for the different non-admin domains. If that's not the case, add these Permissions back.
- Enabling the Drive Advanced Service would be required in this case.
Updated code sample:
function fireOnOpen() {
const adminUsers = ["email1@gmail.com","email2@gmail.com","email3@gmail.com"];
const currentUser = Session.getActiveUser().getEmail();
const internalSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Internal");
if (adminUsers.includes(currentUser)) {
internalSheet.showSheet();
const documentProperties = PropertiesService.getDocumentProperties();
documentProperties.setProperty("lastAdminAccess", new Date().getTime()); // Store time of admin access
} else internalSheet.hideSheet();
}
function createOnOpenTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger("fireOnOpen")
.forSpreadsheet(ss)
.onOpen()
.create();
}
function updatePermissions() {
const fileId = SpreadsheetApp.getActive().getId();
const lastAdminAccess = PropertiesService.getDocumentProperties().getProperty("lastAdminAccess"); // Last time of admin access in ms
const now = new Date().getTime(); // Current time in milliseconds
const thirtyMinutes = 1000 * 60 * 30; // 30 minutes in milliseconds
if (now - lastAdminAccess < thirtyMinutes) {
const currentPermissions = Drive.Permissions.list(fileId)["items"];
const publicPermissionIds = currentPermissions.filter(permission => permission["type"] === "anyone")
.map(permission => permission["id"]);
publicPermissionIds.forEach(permissionId => Drive.Permissions.remove(fileId, permissionId));
} else {
const resource = {
type: "anyone",
role: "reader"
}
Drive.Permissions.insert(resource, fileId);
}
}
function createTimeTrigger() {
ScriptApp.newTrigger("updatePermissions")
.timeBased()
.everyMinutes(5)
.create();
}