How to add Emails as Editors of whole document using a Setup Sheet? - javascript

I'm trying to take my project from a stage to another, and I was able to make some good progress so far.
I've got the following script that runs when the sheet called Setup_Protections is edited: it removes all the sheets protections then add them back with the Emails specified in the Setup sheet (i.e. add those emails as editors of the protected sheets).
But the problem is that the spreadsheet needs to be shared beforehand so they can access it first. Is there a way to share in the same time the document with the emails entered in the Setup sheet ? (without necessary using a method that requires enabling Sheets API as I'll be duplicating many times the documents)
Thank you for your help
Sheet
MY SCRIPT:`
var environment = {
protectionConfigSheetName: "Setup_Protection",
};
// Script fires when Setup_Protection is edited
function onEdit(e) {
if (e.range.getSheet().getName() === environment.protectionConfigSheetName)
resetSpreadsheetProtections();
}
function removeSpreadsheetProtections(spreadsheet) {
[
SpreadsheetApp.ProtectionType.SHEET,
].forEach(function (type) {
return spreadsheet.getProtections(type).forEach(function (protection) { return protection.remove(); });
});
}
function getProtectionConfig(spreadsheet) {
var protectionConfigSheetName = "Setup_Protection";
var sheet = spreadsheet.getSheetByName(environment.protectionConfigSheetName);
var values = sheet.getDataRange().getValues();
var protectionConfig = values
.slice(1)
.reduce(function (protectionConfig, _a) {
var targetSheetName = _a[0], emailAddress = _a[1];
var config = protectionConfig.find(function (_a) {
var sheetName = _a.sheetName;
return sheetName === targetSheetName;
});
var editors = emailAddress.split(",");
if (config)
config.editors = config.editors.concat(editors);
else
protectionConfig.push({
sheetName: targetSheetName,
editors: editors.slice()
});
return protectionConfig;
}, []);
return protectionConfig;
}
function setSpreadsheetProtections(spreadsheet, protectionConfig) {
spreadsheet.getSheets().forEach(function (sheet) {
var protection = sheet.protect();
protection.removeEditors(protection.getEditors().map(function(editor) {
return editor.getEmail();
}));
var currentSheetName = sheet.getName();
var config = protectionConfig.find(function (_a) {
var sheetName = _a.sheetName;
return sheetName === currentSheetName;
});
if (config)
protection.addEditors(config.editors);
});
}
function resetSpreadsheetProtections() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var protectionConfig = getProtectionConfig(spreadsheet);
removeSpreadsheetProtections(spreadsheet);
setSpreadsheetProtections(spreadsheet, protectionConfig);
}
Note: there is also another script needed for this one called Polyfill.gs

Finally it's working now:
Add the following to the above code:
function addEditorsToSpreadsheetFromProtectionConfig(spreadsheet, protectionConfig) {
var editors = protectionConfig.reduce(function (accumulator, _a) {
var editors = _a.editors;
return accumulator.concat(editors);
}, []);
spreadsheet.addEditors(editors);
}
Then Add to resetSpreadsheetProtections() the following line:
addEditorsToSpreadsheetFromProtectionConfig(spreadsheet, protectionConfig);

Related

How do i assign a function to be running in a specific column

I'm developing a Google Sheet spreadsheet to save time at my work routine and I end it up bumping into the script world. This is a very new tool to me and naturally I've been struggling to make it happen.
Currently I'm using Google App script.
I have this function right here:
function insertFollowerCount() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(this.sheetName);
var previousData = prevData();
var followers = getFollowers();
sheet.appendRow([Utilities.formatDate(new Date(), "GMT-3", "dd-MM-yyyy'T'HH:mm:ss'Z'") , followers, followers-previousData.followers]);
};
function getFollowers() {
return parseInt(fetch(instagram_base_url + followers)['data']['user']['edge_followed_by']['count']);
}
var header = {'Cookie': ********}
function fetch(url) {
var ignoreError = {
"muteHttpExceptions": true,
"validateHttpsCertificates":false,
"headers":header
};
var source = UrlFetchApp.fetch(url, ignoreError).getContentText();
var data = JSON.parse(source);
return data;
}
function prevData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(this.sheetName);
var Avals = sheet.getRange("B1:B").getValues();
var Alast = Avals.filter(String).length;
var prevFollowers = sheet.getRange("B"+Alast).getValues();
var prevEngagement = sheet.getRange("H"+Alast).getValues();
return {
followers: parseInt(prevFollowers),
engagement: prevEngagement
}
};
The code that i have take my instagram info and turns into data at my Google sheets. What im looking for is to programing this data to be running at my column 'H' and so on at my Google Sheets every time i run the script

Google Sheets Scripts - run scripts as administrator / owner

I have Google Sheet, name TEST https://docs.google.com/spreadsheets/d/1HsRwknyZBmZZ9nibDfNpOwqkVsFGThDyrTwspV-5_4U/edit?usp=sharing
Sheet: Arkusz 1
Column A: all people can edit
Column B: only owner can edit
Library (for everyone): https://script.google.com/macros/s/AKfycbzpnEMhIG-0dMp54q3W4UxoT71-lSdfF7Qxf7rq_j6gJMNIxuCS/exec
A user cannot add a row because it is blocked by column B, which belongs only to the admin.
How can I create macro, which allow user to add new rows?
I have three scripts:
function insertRow() {
var ss = SpreadsheetApp.getActive()
var sheetName = ss.getActiveSheet().getName()
var row = ss.getActiveRange().getRow()
var numRows = Browser.inputBox('Insert Rows', 'Enter the number of rows to insert', Browser.Buttons.OK);
Logger.log(numRows)
var url ="https://script.google.com/macros/s/AKfycbzpnEMhIG-0dMp54q3W4UxoT71-lSdfF7Qxf7rq_j6gJMNIxuCS/exec"
var queryString = "?sheetName="+sheetName+"&rowNo="+row+"&noOfRows="+numRows
url = url + queryString
Logger.log(url)
var request = UrlFetchApp.fetch(url)
if (request != 'Success')
Browser.msgBox(request)
}
Second:
function doGet(e) {
var param = e.queryString
var parameters = param.split("&")
// This just checks only 3 parameters are present else gives a invalid link
if (param != null && parameters.length == 3){
param = e.parameter
var name = param.sheetName
var row = Number(param.rowNo)
var numOfRows = Number(param.noOfRows)
} else{
return ContentService.createTextOutput("Invalid query")
}
try{
var ss = SpreadsheetApp.openById("https://docs.google.com/spreadsheets/d/1HsRwknyZBmZZ9nibDfNpOwqkVsFGThDyrTwspV-5_4U")
var sheet = ss.getSheetByName(name)
sheet.insertRowsAfter(row, numOfRows);
var source_range = sheet.getRange(row,1,1,sheet.getLastColumn());
var target_range = sheet.getRange(row+1,1,numOfRows);
source_range.copyTo(target_range);
}
catch (err){
return ContentService.createTextOutput("error: "+err)
}
return ContentService.createTextOutput("Success")
}
And after clicked function insertRow and filled number of rows I have doPost(e) information.
Could you help me?
On the solution you provided below, I see that the issue is in mainScript
function mainScript(e) {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet()
// assign the sheet to a variable and use it below instead of spreadsheet
var sheet = spreadsheet.getSheetByName('ZNC')
sheet.getRange('A2').activate()
sheet.insertRowsBefore(sheet.getActiveRange().getRow(), 1);
}
Hmm, I created solution, but I think there's a bug somewhere, because it doesn't add the line, even though everything is correct and the script is published as public.
function ZNCWiersz() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('ZNC'), true);
const activeSheet = SpreadsheetApp.getActiveSheet().getSheetName();
const url = ScriptApp.getService().getUrl();
UrlFetchApp.fetch(`${url}?sheetName=${activeSheet}`, {
headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() },
});
// DriveApp.getFiles() // This is used for automatically detecting the scope of "https://www.googleapis.com/auth/drive.readonly". This scope is used for the access token.
}
// When runScript() is run, this function is run.
const doGet = (e) => ContentService.createTextOutput(mainScript(e));
// This script is run by Web Apps.
function mainScript(e) {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet()
spreadsheet.getSheetByName('ZNC')
spreadsheet.getRange('A2').activate()
spreadsheet.insertRowsBefore(spreadsheet.getActiveRange().getRow(), 1);
}

If GoogleJsonResponseException: then skip and move to next row

I have a working script. need to improvise to have no manual interruption. We have multiple Profiles in Analytics, sometimes we lose access and sometimes we have. So when i run the Script, If we lost access to 1 of 60 profiles, i have to delete that entry manually then rerun the script.
What i want is, If there is below error, Then skip and continue with next row
"GoogleJsonResponseException: API call to analytics.data.ga.get failed with error: User does not have sufficient permissions for this profile."
function GoogleAnalytics() {
var doc2 = SpreadsheetApp.getActiveSpreadsheet();
var dashboard = doc2.getSheetByName("Dashboard");
for(var i=52;i<65;i++){
var viewId = dashboard.getRange(i,13).getValue(); // Your Google Analytics view ID
var metric = 'ga:metric, ga:metric2, ga:metric3';
var option = {'segment': 'gaid::-5'};
var result = Analytics.Data.Ga.get(viewId, metric, option);
var metric = result.totalsForAllResults['ga:metric'];
var metric2 = result.totalsForAllResults['ga:metric2'];
var metric3 = result.totalsForAllResults['ga:metric3'];
var doc = SpreadsheetApp.getActiveSpreadsheet(); // Current document
var sheet = doc.getActiveSheet(); // Current sheet
sheet.getRange(i,14,1,1).setValue(metric);
sheet.getRange(i,15,1,1).setValue(metric2);
sheet.getRange(i,16,1,1).setValue(metric3);
} }
try it this way:
function GoogleAnalytics() {
var doc2 = SpreadsheetApp.getActiveSpreadsheet();
var sh = doc2.getSheetByName("Dashboard");
var sheet = doc2.getActiveSheet(); // Current sheet
const vs = sh.getRange(52, 13, 13).getValues();
var metric = 'ga:metric, ga:metric2, ga:metric3';
var option = { 'segment': 'gaid::-5' };
for (var i = 0; i < vs.length; i++) {
var viewId = vs[i][0]; // Your Google Analytics view ID
try {
var result = Analytics.Data.Ga.get(viewId, metric, option);
}
catch(e){
continue;
}
if (result) {
sheet.getRange(i + 52, 14, 1, 3).setValues([[result.totalsForAllResults['ga:metric'], result.totalsForAllResults['ga:metric2'], result.totalsForAllResults['ga:metric3']]]);
}
}
}
Without the benefit of working data some of this may not be correct but using setValues and getValues should speed it up considerably and the try catch blocks should help with not getting result consistently. Also you want to avoid making unnecessary declarations in loops.
I might understand the question incorrectly (if so, please clarify) but it sounds to me like you just need to add...
function GoogleAnalytics() {
var doc2 = SpreadsheetApp.getActiveSpreadsheet();
var dashboard = doc2.getSheetByName("Dashboard");
for(var i=52;i<65;i++){
try { //...this line and...
var viewId = dashboard.getRange(i,13).getValue(); // Your Google Analytics view ID
var metric = 'ga:metric, ga:metric2, ga:metric3';
var option = {'segment': 'gaid::-5'};
var result = Analytics.Data.Ga.get(viewId, metric, option);
var metric = result.totalsForAllResults['ga:metric'];
var metric2 = result.totalsForAllResults['ga:metric2'];
var metric3 = result.totalsForAllResults['ga:metric3'];
var doc = SpreadsheetApp.getActiveSpreadsheet(); // Current document
var sheet = doc.getActiveSheet(); // Current sheet
sheet.getRange(i,14,1,1).setValue(metric);
sheet.getRange(i,15,1,1).setValue(metric2);
sheet.getRange(i,16,1,1).setValue(metric3);
} catch(e) { //...this part
console.log(e); //optional, catch(e){} is perfectly valid as well, or any code you might want to execute on error
}
} }

How to list all files in Drive that are not "google" mime type using Google Apps Script?

I'v been trying to:
Using Google Apps script, find all files that do not belong to me and is not a google type.
This is what I have:
// 1° try:
var queryStr = "sharedWithMe = true and not mimeType contains 'google'";
var filesIterator = DriveApp.searchFiles(queryStr);
while(filesIterator.hasNext()){
debugger; // it never gets here
}
// 2° try:
var queryStr = "not 'me' in owners and not mimeType contains 'google'";
var filesIterator = DriveApp.searchFiles(queryStr);
while(filesIterator.hasNext()){
debugger; // it never gets here
}
On a Google Drive UI search it's possible to find lots of files on that condition, so there must be something with the query I'm trying.
Thanks
I think this is what you want:
function listNonGoogleMimetypes(id) {
var id=id||'default id';
var files=DriveApp.getFolderById(id).getFiles();
var list=[];
while(files.hasNext()) {
var file=files.next();
var type=file.getMimeType();
if(file.getMimeType().indexOf('GOOGLE')==-1) {
list.push(file.getName());
}
}
Logger.log(JSON.stringify(list));
}
This will do it for the entire drive:
var list=[];
function listNonGoogleMimetypesInDrive() {
getFnF();
Logger.log(JSON.stringify(list));
}
function getFnF(folder) {
var folder= folder || DriveApp.getRootFolder();
var files=folder.getFiles();
while(files.hasNext()) {
var file=files.next();
var type=file.getMimeType();
var name=file.getName();
if(!type.match(/google/i)) {
list.push(file.getName());
}
}
var subfolders=folder.getFolders()
while(subfolders.hasNext()) {
var subfolder=subfolders.next();
getFnF(subfolder);
}
}
This may take a while to go through your entire drive. It took me about 2 minutes but I don't have that much because I just do a lot of programming on my account.
This version also includes owner email
function getFnF(folder) {
var folder= folder || DriveApp.getRootFolder();
var files=folder.getFiles();
while(files.hasNext()) {
var file=files.next();
var type=file.getMimeType();
var name=file.getName();
var owner=file.getOwner().getEmail();
if(!type.match(/google/i) && owner!='your email address') {
list.push(file.getName());
}
}
var subfolders=folder.getFolders()
while(subfolders.hasNext()) {
var subfolder=subfolders.next();
getFnF(subfolder);
}
}

Excel Javascript API fetch position of multiple sheets

How can I get the position of two worksheets using the Excel Javascript API?
Here is how it works just for one sheet:
Excel.run(function (ctx) {
var wSheetName = 'Sheet1';
var worksheet = ctx.workbook.worksheets.getItem(wSheetName);
worksheet.load('position')
return ctx.sync().then(function () {
console.log(worksheet.position);
});
});
=> it logs 0 to the console
But it doesn't logs anything if I try to get the position for two worksheets:
Excel.run(function (ctx) {
var wSheetName = 'Sheet1';
var wSheetName2 = 'Evars';
var worksheet = ctx.workbook.worksheets.getItem(wSheetName);
var worksheet2 = ctx.workbook.worksheets.getItem(wSheetName2);
worksheet.load('position')
worksheet2.load('position')
return ctx.sync().then(function () {
console.log(worksheet.position);
console.log(worksheet2.position);
});
});
I just tried your code, and it works fine. I wonder if you simply didn't have a sheet by one of those names, and so it was throwing an exception -- which was appearing to you as silent, since you didn't have a catch handler.
The code below, essentially the same as yours but with a catch statement, works correctly:
Excel.run(function(ctx) {
var wSheetName = 'Sheet1';
var wSheetName2 = 'Sheet2';
var worksheet = ctx.workbook.worksheets.getItem(wSheetName);
var worksheet2 = ctx.workbook.worksheets.getItem(wSheetName2);
worksheet.load('name, position')
worksheet2.load('name, position')
return ctx.sync().then(function () {
console.log(worksheet.name + ": " + worksheet.position);
console.log(worksheet2.name + ": " + worksheet2.position);
});
}).catch(function(error) {
OfficeHelpers.UI.notify(error);
OfficeHelpers.Utilities.log(error);
})
You can try this snippet live in literally five clicks in the new Script Lab (https://aka.ms/getscriptlab). Simply install the Script Lab add-in (free), then choose "Import" in the navigation menu, and use the following GIST URL: https://gist.github.com/Zlatkovsky/c61594f1c86970e8dba91fe94b7ca4b6. See more info about importing snippets to Script Lab.
Found the solution here ... maybe this will help someone
Excel.run(function (ctx) {
var worksheets = ctx.workbook.worksheets;
worksheets.load('items');
return ctx.sync().then(function () {
for (var i = 0; i < worksheets.items.length; i++) {
var sheet_name = worksheets.items[i].name;
var sheet_position = worksheets.items[i].position;
}
});

Categories