I have an issue trying to remove active user editor from a file.
When I try the follwing code it works just fine :
function testOwner() {
var theFile = DriveApp.createFile('New Text File', 'Hello, world!');
theFile.setOwner('anemail#mydomain.fr');
theFile.removeEditor(Session.getActiveUser().getEmail());
}
But when I'm using the 'makeCopy' method I get a message error that stops the script even if the editor has been removed. Here is the code :
function testOwner2() {
var template = DriveApp.getFileById(TEMPLATE_ID);
var theFile = template.makeCopy('Name');
theFile.setOwner('anemail#mydomain.fr');
theFile.removeEditor(Session.getActiveUser().getEmail());
}
The error message on the removeEditor line : 'Impossible de trouver l'élément correspondant à cet identifiant. Vous n'êtes peut-être pas autorisé à y accéder.' meaning 'Couldn't find any element matching this ID. Maybe you are not authorized to access it'
I didn't find any question on this. Is is a known issue ?
Thanks for your help !
when you .makeCopy(), all the editors get removed for the new file.
in addition to help debug, use Logger some and view the logs...
function testOwner2() {
var template = DriveApp.getFileById(TEMPLATE_ID);
var theFile = template.makeCopy('Name');
theFile.setOwner('anemail#mydomain.fr');
var editors = theFile.getEditors();
for ( var i in editors ) { Logger.log(editors[i].getEmail()); }
Logger.log(Session.getActiveUser().getEmail());
theFile.removeEditor(Session.getActiveUser().getEmail());
}
Try .getEffectiveUser() instead of .getActiveUser() as that's typically a cause of unexpected results as well.
This is to be considered as a comment, just taking a bit more place than a regular one.
There seems to be an issue with the code suggested in the other answer : You just can't remove yourself as an editor (and you automatically become an editor when you set the ownership to another user) when you are not the owner of the copied file anymore... you will get just the same error message.
The first code snippet you give that creates a file should follow the same logic but - as I learned today (thanks for that) - it does not and I really don't know why.
( I hope someone "better informed" will explain... )
I guess the only solution (at least that I can imagine using a script) if you don't want to be an editor of the copy would be to integrate a small script in your template (that would be copied in the copy) to create a menu allowing the new user to call a function that would remove you as an editor.
Or more simply after all, explain how to do it from the Drive Ui itself ;-)
Related
Currently, our Smartsheet system where I work is rather unorganized and buggy. I have just recently inherited it with the task of upgrading our system. A big issue I have been encountering repeatedly as I work with the Smartsheet API is that automation rules are effectively unavailable through API connection (they are all complex automations, no simple ones here). This makes sheet overhauls hard for me. Essentially, as I make new rows, add new automations, add new columns, I have to do it for each and every sheet in our system (about 80 or so right now). Adding new automations individually is a tedious exercise of mental fortitude, and therefore, not the move. The only way I have found to duplicate automations is effective but still time consuming:
1. Make a template with all of the new improvements on the old sheet
2. Make a new sheet from the template for each old sheet that needs to be replaced
3. Download all of the attachments and comments associated with the old sheets and upload them to the new sheets
4. Delete the old sheets
This is the only way I've found to achieve what I need for upgrading old sheets, but its quickly becoming too large to do manually. I'm looking for a solution via API that will accomplish this process automatically. I can code it either with the SDK or in JSON, and I will probably have more freedom in JSON. So far, I've mostly been playing with the SDK, and only recently have I started messing with JSON for pulling attachments in order to save them.
I know from my last question that I can't necessarily use a get sheet and then just update another sheet with the data pulled from the get request, but what about copy sheet? Really the main issue I'm encountering now other than automation issues, is successfully copying or downloading the attachments and uploading them to the corresponding row in the new sheet via code. This is where I started testing with list_all_attachments, but I feel like this all may just be wishful thinking, but that is why I am reaching out to see if there really is a way around it.
Here is the (non-functioning) code I have so far, but it's not really doing much. It's just been for planning and testing different api calls:
coolurl = 'https://api.smartsheet.com/2.0/sheets/################/attachments'
resp = requests.get(coolurl,headers=headers) #new response is equal to the get request; combine the parameters url, and headers
resp_data = resp.json()['data']
response1 = smartsheet_client.Attachments.list_all_attachments(
################,
include_all=True
)
#making a new sheet
response = smartsheet_client.Folders.create_sheet_in_folder_from_template(
################, #folder id of template
smartsheet.models.Sheet({
'name': 'newsheet',
'from_id': ################,
'attachments': resp_data
}),
'all'
)
Please let me know if anyone has any ideas, thank you!
After almost a month of tooling around, I have finally wrote a program that will do as I described in my question. At first glance it seems that Smartsheet has limited API functionality. While I think there are areas that there could be more, I recognize that when paired with a solid understanding of programming and lots of help from stack, a lot can be accomplished. I am learning Python for the purpose of Smartsheet's API, and as a beginner in both of those subjects, there is a reason why I sat on this for a month.
Below is my code for this answer:
#grab all of the relevant sheets
folder = smartsheet_client.Folders.get_folder(
##################) # folder_id of the project sheets
for sheets in folder.sheets:
oldsheetids.append(str(sheets.id))
print(oldsheetids)
for index in range(len(oldsheetids)):
indexes.append(index)
print(index)
#this function returns the last element in the newsheetids
def linkup():
return int(newsheetids[-1])
def bigupdate():
#loop through the process for each sheet, the 0:3 was for testing the first 3 sheets
for oldsheets in oldsheetids[0:3]:
global newsheetids #these are global because I was testing different approaches for linkup()
newsheetids = []
global newsheetnames
newsheetnames = []
#pull the old sheets from the folder, one at a time
global pulled_sheet
pulled_sheet = smartsheet_client.Sheets.get_sheet(
int(oldsheets), # sheet_id -
include = 'all'
)
#make a copy of the template sheet were using for the update
response1 = smartsheet_client.Sheets.copy_sheet(
groundzeroid, # sheet_id of the template
smartsheet.models.ContainerDestination({
'destination_type': 'folder', # folder, workspace, or home
'destination_id': folderzeroid, # folder_id of the new workspace
'new_name': pulled_sheet.name
}),
include = ['rules','rulerecepients'],
)
print('creating a new sheet for: ' + pulled_sheet.name)
time.sleep(1)
#grab the folder of the new sheets, need to pull all those sheet ids
folder2 = smartsheet_client.Folders.get_folder(
###################) # folder_id of the new updates folder
for sheets1 in folder2.sheets:
newsheetids.append(str(sheets1.id))
newsheetnames.append(str(sheets1.name))
#here we iterate through to grab all the row ids from all the existing rows
goodrow_ids = []
for row in pulled_sheet.rows:
if row.cells[0].value in testvalues:
goodrow_ids.append(str(row.id))#,str(row.row_number)])
print("another row prepped!")
print(goodrow_ids)
print('goodrow_ids printed!')
result = linkup()
print(result)
#copy all the rows to the new sheet
for rowid in goodrow_ids:
response = smartsheet_client.Sheets.copy_rows(
int(oldsheets), #sheet id of row we are copying
smartsheet.models.CopyOrMoveRowDirective({
'row_ids' : int(rowid), #rowid of row to copy
'to' : smartsheet.models.CopyOrMoveRowDestination({
'sheet_id' : int(result) #destination sheet id
})
}),
include = ['all'], ignore_rows_not_found = True
)
print("rows loading in...")
print(pulled_sheet.name + ' new sheet created!')
sheetcounter + 1
time.sleep(5)
bigupdate()
This code satisfies the first three objectives of my question. I did not put any deletion code in this because it seemed unnecessary given how easy it is to do regularly on the site. I realize there are prolly broken bits or redundancies, but as far as functionality goes, it's great for my needs. I tried to leave comments when necessary to explain what's happening in the code. I hope some of this helps!
I want to take the text for a document and save it as a variable. I looked in the documentation and found "getText" something I think shall work. https://developers.google.com/apps-script/reference/document/footnote-section#gettext
I just get a problem when I try using it, because it's not a pre built function it gives the error massage "TypeError: Cannot read property 'getText' of null". So I looked at some more into it and noticed I needed Authorization:
"Scripts that use this method require authorization with one or more of the following scopes:
https://www.googleapis.com/auth/documents.currentonly
https://www.googleapis.com/auth/documents"
So how do I get the required authorization, do I need to do something different or is there another way i could do it?
It's just going to run on some of my docs for fun to se what funny things I am able to do with the program.
(New to programing, now the basics but just trying to see if programing is something for me)
Thanks in advance
Given with this sample document
You can start with this sample code below.
Code:
function myFunction() {
var body = DocumentApp.getActiveDocument().getBody();
var text = body.editAsText();
Logger.log(text.getText()); // returns all text in document ("Hey, search for me!!! I am <here>!!")
// 1. Regular expression (exec)
var regExp = new RegExp("<\(.*\)>", "gi"); // "i" is for case insensitive
var search = regExp.exec(text.getText())[1];
Logger.log(search); // returns "here"
// 2. (search)
Logger.log(text.getText().search(/here/)); // returns the index where the string was found, or -1 if not found
}
Output:
Note:
getText will return the text of the Class Text.
If you want to get a specific pattern from the document, you need to use Regular expression. I prefer the exec method. See usage for more details
For the authorization, you only need to click allow. I assume you are using apps script due to the tag.
I am currently working on external app using Google Sheets and JSON for data transmission via Fetch API. I decided to mock the scenario (for debugging matters) then simple JSON comes from my external app through prepared Code.gs to be posted on Google sheets. The code snippet I run through Apps-scripts looks like this:
function _doPost(/* e */) {
// const body = e.postData.contents;
const bodyJSON = JSON.parse("{\"coords\" : \"123,456,789,112,113,114,115,116\"}" /* instead of : body */);
const db = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
db.getRange("A1:A10").setValue(bodyJSON.coords).setNumberFormat("#"); // get range, set value, set text format
}
The problem is the result I get: 123,456,789,112,113,000,000,000 As you see, starting from 114 and the later it outputs me 000,... instead. I thought, okay I am gonna explicitly specify format to be returned (saved) as a text format. If the output within the range selected on Google Sheets UI : Format -> Number -> it shows me Text.
However, interesting magic happens, let's say if I would update the body of the JSON to be parsed something like that when the sequence of numbers composed of 2 digits instead of 3 (notice: those are actual part of string, not true numbers, separated by comma!) : "{\"coords\" : \"123,456,789,112,113,114,115,116,17,18\"}" it would not only show response result as expected but also brings back id est fixes the "corrupted" values hidden under the 000,... as so : "{"coords" : "123,456,789,112,113,114,115,116,17,18 "}".
Even Logger.log() returns me initial JSON input as expected. I really have no clue what is going on. I would really appreciate one's correspondence to help solving this issue. Thank you.
You can try directly assigning a JSON formatted string in your bodyJSON variable instead of parsing a set of string using JSON.parse.
Part of your code should look like this:
const bodyJSON = {
"coords" : "123,456,789,112,113,114,115,116"
}
I found simple workaround after all: just added the preceding pair of zeros 0,0,123,... at the very beginning of coords. This prevents so called culprit I defined in my issue. If anyone interested, the external app I am building currently, it's called Hotspot widget : play around with DOM, append a marker which coordinates (coords) being pushed through Apps-script and saved to Google Sheets. I am providing a link with instructions on how to set up one's own copy of the app. It's a decent start-off for learning Vanilla JavaScript basics including simple database approach on the fly. Thank you and good luck!
Hotspot widget on Github
I have a Google doc which was created in Word and imported. I want to locate a 'marker' in the text so that I can insert a table at that point. However, I am unable to progress because my script keeps falling over at the "findText()" point. I have looked at a number of answers to similar problems on here and used what I think are identical methods, but it still falls over!
Rather than post code here, it seems clearer to post a screenshot of exactly what I get when I attempt to run the script. The first two items in the execution log prove that a) the document is being opened correctly and b) that the body is being correctly identified. There is definitely a "$" sign later in the text, so why am I getting the error?
Script
var doc = DocumentApp.openByUrl("https://docs.google.com/document/d/###/edit");
var body = doc.getBody();
var tblLoc = body.findText("$");
console.log(tblLoc)
Modification points:
From your following image,
I thought that the reason of your issue is due to $ of body.findText("$").
In this case, I think that it is required add \\ like body.findText("\\$")
The method of findText() returns the object of DocumentApp.RangeElement. So when you use console.log(tblLoc) and when the value is retrieved, {} is shown in the log.
When you want to see the retrieved value, for example, you can use console.log(tblLoc.getElement().asText().getText()).
When above points are reflected to your script, it becomes as follows.
Modified script:
const doc = DocumentApp.openByUrl("https://docs.google.com/document/d/###/edit");
const body = doc.getBody();
const tblLoc = body.findText("\\$");
console.log(tblLoc.getElement().asText().getText())
Reference:
findText(searchPattern)
I know I am not alone out there in having some issues with Google's deprecation of DocsList in favor of DriveApp. I have replaced all references to DocList with DriveApp in my code.
I have a spreadsheet containing variables that used to merge nicely with a Google Docs template, effectively a mailmerge.
Here are the declared variables:
var myDataSheet, myVariablesSheet;
var rowId, maxRows, maxRowsOverride;
var templateDocId;
var timeZone, timestamp, dateline;
var newFolder, newFolderId, collectionDate, collectionName,appendTimestamp;
var newDocNameBase, newDocNameSuffixCol, newDocName, newDoc, newDocId;
var fieldColRow, fieldArr, colArr;
When I run the script, I am returning an error identified at line 165 of my code. The error states:
TypeError: Cannot find function addFile in object Copy of Letter of
Rep Template. (line 165, file "Mail Merge")
Line 165 reads:
DriveApp.getFileById(newDocId).addFile(DriveApp.getFolderByName(newFolderId));
Oddly, (at least to me) when I run the script I get a single outputted merged document, but never more than one.
I suspect that I am dealing with a failing loop of some kind, and an issue with my file naming and my destination folder, but I cannot get past where I am... any insight, help or just a straight fix greatly appreciated.
The getFolderByName() method returns a Folder Iterator. In other words, it returns an "object" that can have multiple elements in it. Even though you may only have one folder with a given name, you still need to use a loop to access the one folder.
Google Documentation - Folder Iterator
You are not going to be able to chain all the methods together that you have in that one line of code giving you the error.
I saw a few mistakes with your code.
DriveApp.getFolderByName(String) looks for folders with the given name, and returns multiple folders. You seemed to have passed in the folder's id. A folder's id and its name are different. You can create a folder named "MyFolder", and another folder called "MyFolder". Their names will be the same, but their id's will be unique.
You're calling File.addFile(Folder);, which doesn't exist. That's why you're getting that error. You should switch it around and use Folder.addFile(File) instead. Here's the fixed code:
DriveApp.getFolderById(newDocId).addFile(DriveApp.getFileById(newDocId));
This question is a bit old but I hope I could help :)