I have a Google Sheet where other users in my organisation have edit access. One annoying problem we're facing is users changing the sizes of the columns.
How do I prevent users from changing the size of any column in the sheet? I am open to using Google Apps Script to implement a solution.
You could try to make use of the "On Change" trigger. It detects a couple of things, one of which is if you modify a column width. The 'changeType' will be "OTHER".
Unfortunately, there isn't an easy way to see what column was re-sized, so you'll have to just reset every column back to whatever the default is. Additionally, this will also trigger for edits, inserting/deleting columns, etc. So you'll have to choose to what level a user can modify the spreadsheet, and otherwise, bailout of the function:
function onChange(e) {
if (e.changeType != "OTHER") {
return;
} else {
//otherwise re-size columns
}
}
Also, it's an installable trigger, so you'll have to enable it. From the script editor:
Resources -> Current project's triggers
Click the link for "No triggers set up..."
If you already have an installed trigger, click "Add a new trigger"
Select which function to Run when a change is detected.
Select 'From spreadsheet' under 'Events'
Select 'On Change' in the final drop-down box.
Otherwise, an installable time-based trigger is also an option, as others have suggested.
Related
I am running a python script that updates my google sheet (named 'Notifications') through the sheets api every 5 minutes. The python script first clears and resizes the sheet to only two rows and then adds the new data on top and the sheet's size gets automatically expanded. I've done that not to have empty rows if my new data is smaller in size than the old one.
So the sheet is updated every 5 minutes but if nothing new happened in my data source, the new values are going to be the same as the old. In particular, the value in 'A2' is going to be the same as before the update. If something new has happened, the value in 'A2' is going to be different. I want to track that and get a pop-up message that notifies me that something new has happened after the update.
So as far as I understand I have to use onChange because onEdit only works if the changes are made by a human and cannot track changes made by the api. I'm uncertain how to do that though.
With onEdit I've achieved it and get a pop-up if I modify the content of 'A2' manually but nothing happens when python updates my sheet.
Here's the code with onEdit:
function onEdit(e) {
if (e.range.getA1Notation() === 'A2') {
let oldValue = e.oldValue
let newValue = e.newValue
if (oldValue != newValue) {
Browser.msgBox('The value in A2 changed from ' + oldValue + ' to ' + newValue);
}
}
}
So the end goal is to get a pop-up not every time python updates my sheet, but only if there's a change in the value of 'A2' after the update.
How can I do this?
The only way to get a pop-up when a a change is made through an API is by using polling function from client-side code. One way to do this is by using a sidebar to hold the client-side code.
You should store somewhere the old values then compare with the current values.
Related
How do I make a Sidebar display values from cells?
How to get all old values for a mulitple cell range with onEdit trigger
onEdit(e) not generating trigger event when cell value changes due to inbuilt function
You can use onChange trigger with a "ValueInputOption"="USER_ENTERED" to trigger a function when a script edits a sheet. But you can't show anything in the UI from the triggered context so getting a pop up will be impossible.
I am trying to document in a certain column, for each row, the user info as it is shown when using the "show edit history" option (by right-clicking a cell).
More specifically- for a given row, if the cell in column G was last modified by me- the cell in column B should say "Yaniv A" or "yaniva#emaildomain.com". Of course this should apply the same way for any other editor.
New information should replace any old in column B one anytime the coresponding cell in column G is modified.
On Apps Script you can set up an onEdit trigger that retrieves the username of the editor and cell where the change was made; but you will have to keep in mind that there may be cases where the username is not retrievable (mainly if the user haven't accepted the script scopes), you can read more about those situations on getActiveUser docs.
You can achieve what you are requesting with the following function. It will use the event object to check if the change was made on the G column, and if it was, the email of the user (or Unknown if the scopes weren't accepted) will be written on the B column of the same row.
function onEdit(e) {
var range = e.range;
var editor = e.user.getEmail();
if (editor == '' || editor == null) {
editor = 'Unknown';
}
if (range.getA1Notation().search('G') != -1) {
range.offset(0, -5).setValue(editor);
};
}
After saving this function you will have to create a installable trigger. To do so, go to Edit 🡆 Current project's triggers. After that, click on + Add Trigger and fill this settings:
Choose which function to run: onEdit
Choose which deployment should run: Head
Select event source: From spreadsheet
Select event type: On edit
Failure notification settings: as you wish
After saving the trigger you could test the function by yourself modifying a cell in the G column. Please, do not hesitate to ask for any clarification about my answer or the code itself.
I add an onChange trigger to my Google Sheet via Tools->Script Editor -> Edit -> Current Project's trigger ->... I thought onChange is only triggered when the structure of data/sheet is changed, e.g., add or delete row and/or columns. However, the testing shows the onChange is triggered when cell value is changed as well.
So the onChange trigger behaves almost identical to the onEdit trigger. I am trying to avoid the onEdit trigger since it's triggered too often, which drags down the speed of Google Sheet.
Ideally, I'd like the onChange only being triggered when new rows and/or columns are added. Any help would be highly appreciated!
There is an event object which contains information about the context that caused a trigger when it is fired. For the case of Google Sheets, the event object has a changeType parameter which is a string describing the type of the change. As described in the Event Objects Documentation, this can take the string value of:
EDIT
INSERT_ROW
INSERT_COLUMN
REMOVE_ROW
REMOVE_COLUMN
INSERT_GRID
REMOVE_GRID
OTHER
Cooper's answer will work although as there are other change types including the catch-all OTHER, it will also make the function fire on edits such as sorting ranges or inserting images. Try something like:
function onChange(e){
if(e.changeType == 'INSERT_ROW' || e.changeType == 'INSERT_COLUMN'){
// your code here
}
else {
return;
}
}
And then set up your installable trigger as normal in Edit > Current project's triggers > + Add Trigger.
Do something like this
function myOnchangeFunction(e) {
if(e.changeType=='EDIT') {
return;
}
}
I wrote some GAS code for a person and it required a trigger.
I created this line and asked the person to call it to set up his trigger (as it is his spreadsheet):
ScriptApp.newTrigger("myFunction").timeBased().everyDays(1).inTimezone("Australia/Brisbane").atHour(5).create()
But then, I cannot see his trigger.
To see if a trigger is there I used:
Logger.log(ScriptApp.getProjectTriggers())
It returned an empty array so looks like the trigger is gone but I don't see it physically...
So, if it's a client, it is not cool to ask his "please set up your trigger" and then not be able to see it and then ask to check if it's there.
What's the best way to set up and manage triggers for clients?
EDIT: As stated in the accepted answer, It seems other user's triggers are viewable manually in the dashboard.
Other user's triggers are not available for you to view programmatically/manually for security reasons.
I created this line and asked the person to call it to set up his trigger (as it is his spreadsheet):
You must explicitly create a menu , so that the user may click on it to create the trigger:
If the user sets up a trigger through menu options, You can set the id of trigger to properties service like this.
function createTrigger(){ //to be linked to a "Create Trigger" menu
// Creates an edit trigger for a spreadsheet identified by ID.
var tid = ScriptApp.newTrigger('myFunion')
.forSpreadsheet('1234567890abcdefghijklmnopqc3')
.onEdit()
.create()
.getUniqueId();
//set id of trigger in properties
PropertiesService
.getScriptProperties() //if you want public access to trigger(If not use getUserProperties)
.setProperty(
Session.getEffectiveUser().getEmail() ||
('user1 ' + Math.random().toFixed(3)),
tid
);
//Inform user: trigger created successfully.
SpreadsheetApp.getActive().toast('Trigger created successfully with id: ' + tid);
//#see https://script.google.com/home/triggers
}
To see his triggers, open the editor and choose "Current Project Triggers" from the Edit menu.
On the trigger page, click the "x" on the "Owned by me" filter. Then you shold see all the triggers for the project and know if it is set up. It won't tell you who owns it, I don't think.
You cannot "manage" his triggers from there, just see them. There is no way to mess with other peoples triggers (a much debated topic!) but you can change the name of the function to break a trigger.
So, i made a spreadsheet for Minecraft and 'Feed The Beast' a modpack for the game. My spreadsheet lists all the mods that are updated for the latest version of Minecraft. Recently i made a script to go out and parse pages for mod version numbers. They change hourly-daily and i rather not do it manually.
function GetVersion(url, slice, num1, num2) {
var r = UrlFetchApp.fetch(url).getContentText();
var version = r.slice(r.indexOf(slice)).slice(num1, num2);
return version;
}
In one of the cells Ill have the following
=GetVersion("http://files.minecraftforge.net", "Build 7.", 12, 15)
Now this works fine and gets me the version number im looking for. The problem is, when an update happens and a new version comes out, the parser doesn't reflect this even if i close the window and reopen the spreadsheet or reload or whatever else! If I change the above slightly, like change the 15 to 16, it will refresh, but then will be stuck there again until i manually change it again.
How do I get it so it at least refreshed when i reload the sheet?
Edit: Alright first I tried to go into reosources and triggers and make a trigger very min and everytime the doc is opened. This didn work..............
Then I tried to get clever. Noticing that the formula reevaluates whenever I change it, i surmised that the formula itself or the cell parameters needs to change in order for this to happen. So I pass a dummy parameter though in the formula and change that parameter to update the cell.
But that's annoying and make me have to edit (or press a button) just to get shit to refresh.
So i got a brainwave and Im now passing GoogleClock() in the dummy parameter. All cells now update on their own every 60 seconds. yay
Is there a better way to do this?
Using googleclock as a param is the best u can do if u want to use a custom cel formula.
Instead call an update function from a menu item and/or onOpen / time trigger.
Aside from the workaround of passing GoogleClock() as a parameter (which I agree is the best you are going to do with a custom function), the other option is to do away with a custom function, and use GAS to write the result directly to a cell, eg:
SpreadsheetApp.getActiveSpreadsheet().getRange('Sheet1!A1').setValue(version);
Then in Resources, Current project's triggers, you can set both an "on edit" and time-driven trigger (to be clear: these triggers won't work on a custom function called from a spreadsheet cell; all the "work" must be done by the script itself).