.geteditors and Groups - google-apps-script

I"m trying to run a script against DocsList that will gather the list of viewers and editors and in turn perform some "work" against them. (Specifically, I'm trying to strip rights from the file in question).
It's works perfectly for users. For groups though, it returns the group name and not the email.
I can't find any way using apps scripts to retrieve the group email address from that information.
If I run fileObject.removeEditor(Group Name) is tells me it's an invalid email (which is entirely true).
I'm open to suggestions... I'm completely stuck here.
Alternatively, I'm open to a way I haven't thought of to remove all sharing rights to a bunch of files in Google Docs.
function getDocs(){
var myFolders = DocsList.getAllFolders();
var myFiles = DocsList.getAllFiles(0,10);
var mySharing = new Array();
for(x in myFiles){
mySharing[x] = [myFiles[x].getId(), myFiles[x].getEditors(), myFiles[x].getViewers()];
for(y in mySharing[x][1]){
if(mySharing[x][1][y].toString() != "doc.owner#deltahotels.com"){
myFiles[x].removeEditor(mySharing[x][1][y]);
}
}
for(y in mySharing[x][2]){
if(mySharing[x][2][y].toString() != "doc.owner#deltahotels.com"){
myFiles[x].removeEditor(mySharing[x][1][y]);
}
}
}
}

Thanks for bringing this up. This is a problem with the way the getEditors/getViewers methods handle groups. I've raised this issue for you in the issue tracker. Please star it to be updated on the progress.

Related

Creating a UrlFetchApp script to replace the Google Sheet importHTML function

I used the following formula for about a year now and suddenly it stopped working/importing the table.
=IMPORTHTML("https://tradingeconomics.com/matrix";"table";1)
It gives me a "Could not fetch url: https://tradingeconomics.com/matrix" error.
I tried various things and one of the interesting findings was that the importHTML works for the cached version, but only in a new sheet under a different Google account. Furthermore, the cached version breaks randomly too.
Thus, it seems I won't get around using a script for this purpose.
Ideally, this script would be flexible enough, where it would have a dedicated function e.g. importHTMLtable where the user can input the URL and the table no. and it works. So it would work for the following functions I currently use e.g.
=importHTMLtable("https://tradingeconomics.com/matrix";"table";1)
OR
=importHTMLtable("https://tradingeconomics.com/country-list/business-confidence?continent=world";"table";1)
OR
=importHTMLtable("https://tradingeconomics.com/country-list/ease-of-doing-business";"table";1)
etc...
Not sure if this Github code solves this problem. It seems to only parse text?
As I would assume this is a fairly common problem users of Google Sheets have and would think there might already be an AppScript out there that does exactly this and might be faster in terms of importing speed too.
I can't program, so I tried copying and posting codes to see if I can get some code to work. No luck :(
Can anyone provide a code or maybe an existing app script (I'm not aware of) that does exactly this that?
Try this way
=importTableHTML(A1,1)
with
function importTableHTML(url,n){
var html = UrlFetchApp.fetch(url,{followRedirects : true,muteHttpExceptions: true}).getContentText().replace(/(\r\n|\n|\r|\t| )/gm,"")
const tables = [...html.matchAll(/<table[\s\S\w]+?<\/table>/g)];
var trs = [...tables[n-1][0].matchAll(/<tr[\s\S\w]+?<\/tr>/g)];
var data = [];
for (var i=0;i<trs.length;i++){
console.log(trs[i][0])
var tds = [...trs[i][0].matchAll(/<(td|th)[\s\S\w]+?<\/(td|th)>/g)];
var prov = [];
for (var j=0;j<tds.length;j++){
donnee=tds[j][0].match(/(?<=\>).*(?=\<\/)/g)[0];
prov.push(stripTags(donnee));
}
data.push(prov);
}
return(data)
}
function stripTags(body) {
var regex = /(<([^>]+)>)/ig;
return body.replace(regex,"").replace(/ /g,' ').trim();
}
url-fetch-app#advanced-parameters
matchAll

Remove Location Targets in Bulk - Google Ads Scripts

After exhausting all options, I'm hoping for some help. I am trying to adjust my script to remove all targeted locations for a specific campaign. The problem is, the only way I've found to do it is to iterate through each targeted location and remove them one by one. If a campaign has 2000 location targets, this takes far too long and the script times out.
Below is an example of the standard script used to remove them one by one. Any suggestions on how to speed this up? Is it possible to pass an array of campaign specific locationIDs in and have it remove them all in one shot? Or, is there a function available to remove ALL locations at once? Either of these methods would work for me but I haven't been able to find a solution that accomplishes this.
Thanks in advance for your help
function removeTargetedLocations(campaign) {
var campaignIterator = AdWordsApp.campaigns()
.withCondition('Name = ' + campaign + '')
.get();
if (campaignIterator.hasNext()) {
var campaign = campaignIterator.next();
var locationIterator = campaign.targeting().targetedLocations().get();
while (locationIterator.hasNext()) {
var loc = locationIterator.next();
loc.remove();
}
}
}

Shared spreadsheet by link & unprotected range

Here is my problem. I am generating some sheets in a shared Spreadsheet document. It's shared by link. The idea is to lockdown everyone except the Owner of the spreadsheet. All ranges to be locked down has been locked by the following code :
range.protect().setDescription("Locked");
All other ranges (that can be modified by anyone) are left without calling protect() on them.
So I used the examples at Protection class and I come with this code that I ran in debug mode as the owner :
let rangeProtect = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (let i = 0; i < rangeProtect.length; i++) {
let protection = rangeProtect[i];
if (protection.canEdit() && (protection.getDescription() == "Locked")) {
let editors = ss.getEditors();
for (let j = 0; j < editors.length; j++) {
ss.removeEditor(editors[j]);
}
// Add only owner
ss.addEditor(ownerEmail);
}
}
Unfortunately this is not working. Every person who use the link can still edit everything. But if I modify by hand each protected range with the name "locked" to 'owner only' it works...
So can you tell me what I am doing wrong ?
I tried also this approach:
let protection = sheet.protect().setDescription('Locked');
let unprotected = sheet.getRange("B1:C10");
protection.setUnprotectedRanges([unprotected]);
The problem stay the same :
If I setup the shared link to "View" i cannot modify anything despite the setUnprotectedRanges() call. Everything is locked.
If I setup the shared link to "Edit", I can see that the sheet is locked correctly with exception of that range (as seen when opening the "ranges and sheets protected" from Data menu) but still all person using the link can modify anything in the sheet.
I am stuggling with this problem for days now and I'm running out of idea.
I really tried a LOT of things. So if you know how to do this properly could you give a piece of working code/shared document sample to accomplish this ? Let's say :
Create two ranges with "Locked" description (ex: First Row & First Column)
Set the correct protection on "Locked" ranges (Edition by Owner only)
Anybody with the link could modify all but locked ranges
Using a shared document by link
Any help would be greatly appreciated ! Really !
Note: This code is written in Typescript because I'm using CLASP that convert everything into Google Apps Script's code.

Apps script onedit triggered too fast causing code conflicting with itself

I'm trying to make an applet in Google Spreadsheets and Apps script that scrapes view data about youtube videos searched by users. When a user types in a search query, a new sheet should be copied from a template sheet and customized to the query. The problem I'm encountering is that when a user rapidly types in multiple queries in succession, the script would dump multiple copies of the template sheet and name them 'Copy of template 1', 'Copy of template 2' and so on, whereas the name of each sheet should be "KW: " + its associated keyword. I suspect this is because the function duplicates and renames a sheet, but if two or more instances of a function try this at nearly the same time, they target the same sheet, causing errors.
This is the culmination of what I've tried:
function main(e){
//spreadsheet=SpreadsheetApp.getActiveSpreadsheet() at the top of the function
//keyword=the string the user typed in
//...
while(true){
var random=Math.random().toString();
try{
//assign the template sheet a random name other processes will fail when they try to use it
spreadsheet.getSheetByName('template').setName(random);
//make a copy of the template
spreadsheet.getSheetByName(random).copyTo(spreadsheet);
//give the copy a proper name
spreadsheet.getSheetByName('Copy of '+random).setName("KW: "+keyword);
//reset the name of the template so other processes can use it
spreadsheet.getSheetByName(random).setName('template');
break;
}
//when a process fails, it should wait then try again
catch(e){Utilities.sleep(Math.round(random*250));}
//...
}
main is has a trigger set on edit. The above code prevents any 'Copy of template n' sheets from appearing, but it simply leaves out most of the sheets that should be produced. My guess is that the code encounters an error in the first line of the try block and loops until it times out. I'm very much at a loss as to what to do. I'd appreciate any help, thank you!
Try copying the template sheet first, then changing the name of the new sheet. Your solution runs into errors because you're modifying the template sheet, but you should really avoid doing that. With this approach, you'll never modify the template sheet.
function main(e){
//spreadsheet=SpreadsheetApp.getActiveSpreadsheet() at the top of the function
//keyword=the string the user typed in
//...
while(true){
try{
// Select the template sheet
var templateSheet = spreadsheet.getSheetByName("template");
// Set the new sheet name
var sheetName = "KW: "+keyword;
// Copy the template sheet and set name
var newSheet = templateSheet.copyTo(spreadsheet).setName(sheetName);
break;
}
//when a process fails, it should wait then try again
catch(e){Utilities.sleep(Math.round(random*250));}
//...
}
}
Granted that I don't have full knowledge of what you're doing with your script, I do have some major concerns with your approach. I don't think you should be creating a new sheet for every query. For one, that will make your data really challenging to aggregate. Secondly, since sheet names must be unique, any time someone searches for something that already has an existing sheet, your function will get stuck in the infinite while loop you set up.
I don't know what kind of data you're placing in the sheets, but you should consider trying to record the data in one sheet. Also, although your while loop works and will, in the case of errors, eventually be terminated by Google's script limitations, you should really try (1) implementing something more robust like appending a number to the sheet name and (2) properly logging the errors.

google script reject spreadsheet submit

I have a function in a spreadsheet based script that is triggered when a submission is made with the spreadsheet form :
function onEntry(e){
Logger.log(e);
MailApp.sendEmail("scriptadmin#uniben.edu", "New Mail Request", "Someone submited data");
}
How can I reject the entry, say if it's a duplicate entry ?
Using the documentation on events you will have to choose what data you want check (user name, specific field...) and compare that to data already in the spreadsheet.
You should do these iterations on an array level since it will be far more efficient and fast, you can get data in an array using something like
var data = SpreadsheetApp.openById(key).getDataRange().getValues();
You could also use javascript function like indexOf() that will return -1 if no match if found or item position in the array if a match is found.
Actually there are many ways to do that but your question is too vague to know what will be the best...
EDIT : following your comment, I'd suggest you let the duplicate form data come into the sheet and then use a script to remove duplicates. You could run this script on a on form submit trigger or on a timer to let it run daily or hourly, and send the email only if the last entry was a new one (no duplicates found)... depending on your use case.
There is a script in the gallery that does the job pretty well, it was written by Romain Vialard, a GAS TC that has contributed a lot. (the link above goes to the script description but you can get it also in the public gallery, just search for 'remove duplicates' you'll see that other scripts do that, all the scripts in the gallery have been checked by the GAS team)
4 months late, but better late than never. I believe this function does almost what was originally requested. i.e. "How do I prevent the entry from entering the spreadsheet if I decide that it's a duplicate." It is not precisely what was requested, but very close.
This code checks one column against that same column in another sheet, for all rows in that sheet. Lets say you have a list of companies or clients on a sheet. That list includes name, phone, address, etc. etc. Lets say you want to check against the phone number - if the phone number you are currently entering is already on your client sheet, then don't allow entry - or more precisely clear it out immediately upon entering it.
I'm sure the more experienced members will be able to point out flaws, but it works for me.
I believe it will also work for the case where a phone number in the middle of the sheet is changed - so it's not just last line that gets checked, it's the line that gets edited that gets checked - I've not tested this particlar scenario. Also, I made some changes to variable names to protect the innocent...hopefully I didn't mess anything up while doing that.
I call this function from within another function that is triggered by onEdit. Theoretically it should be able to be installed as an onEdit trigger itself. I hope someone finds it useful.
function checkNewEntryForDuplicate(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var entrySheet = SpreadsheetApp.getActiveSheet();
var clientSheet = ss.getSheetByName("Clients");
var r = entrySheet.getActiveCell();
var lastCol = entrySheet.getLastColumn();
// If this had any consistency, we'd be able to get the row from entrySheet the same
// as we get column. But there is no getRow() method at the sheet level.
var rowNum = r.getRow();
var clientData=clientSheet.getDataRange().getValues();
var phoneColumnOffset=getPhoneColumnOffset(); // You'll need to get the offset elsewhere. I have a function that does that.
var columnNum=e.range.getColumn(); // column that is currently being edited
if (columnNum != phoneColumnOffset+1) // no point in doing anything else if it's not the column we're interested in.
return 0;
var entryRow=entrySheet.getRange(rowNum, 1, 1, lastCol);
var phoneNum = e.range.getValue();
// iterate over each row in the clientData 2-dimensional array.
for(i in clientData){
var row = clientData[i];
var duplicate = false;
// For each row this conditional statement will find duplicates
if(row[phoneColumnOffset] == phoneNum){
duplicate = true;
var msg="Duplicate Detected. Please do not enter. Deleting it..."
Browser.msgBox(msg);
entryRow.clearContent();
entryRow.clearComment();
return duplicate;
}
}
return duplicate;
}
I am doing the same things but having no scripts at all and just by spreadsheet functions. That kind of things are just like SQL for me and very interest to do.
For your question, this link will help: http://www.labnol.org/software/find-remove-duplicate-records-google-docs/5169/