Can someone help me fix the following code?
I using this script to disable editor to copy my Google spreadsheet
function onOpen () {
const ss = SpreadsheetApp.getActive();
const id = ss.getId();
if (id !== '1ld8aPE5zVBYoT39WPzZjWxU1uHJ8lVVV5vkBiv5USzI') ss.getSheets().forEach((s) => ss.deleteSheet(s)); }
Reference.
Disable Spreadsheet copy - Google Sheets
I tried copying to another worksheet but code wont work
Example
https://drive.google.com/drive/folders/11Jqh_xaw0CdI1cBUL_hZwCjOJNnRKSU6?usp=sharing
Tl;Dr There is no way to prevent that spreadsheet editors copy the spreadsheet, because of this, in order to prevent that editors copy the spreadsheet you should use another approach to whatever you are doing with your spreadsheet.
Regarding the code in the question, it doesn't disable spreadsheet copy, what it does is, if the spreadsheet id is not equal to certain value, iterates over all the sheets but as it's not possible to delete all the sheets as each spreadsheet should include at least one sheet, the script throws an error. You might change the script i.e. inserting a new sheet and delete the other sheets, but the editor of the original spreadsheet, as there are the owner of the copy, they will be able to delete the script and restore the deleted data from the version history of the new spreadsheet.
Related
Disable Spreadsheet copy - Google Sheets
At least, you can add a sheet with an alert and delete all other sheets
var id = '1ld8aPE5zVBYoT39WPzZjWxU1uHJ8lVVV5vkBiv5USzI'
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
if (ss.getId() != id) {
var gid = ss.insertSheet().getSheetId()
ss.getSheets().forEach(function (sh) {
if (sh.getSheetId() != gid) { ss.deleteSheet(sh) }
})
}
}
I am not sure that there is no bypass! See Rubén's advice.
Related
In my google sheet I have some data in it. Later I published my sheet as .csv format.
Pls find my sheet here, below are the Cell Values
C1 = tdsyltt = 'ఈరోజు ( బుధవారం ) క్విజ్ సిలబస్' ;
C2 = tdsyl = 'హబక్కూకు 1 & యాకోబు 2, 3' ;
C4 = document.getElementById("tdsyltt).innerHTML = tdsyltt ;
C5 = document.getElementById("tdsyl").innerHTML = tdsyl ;
And using my published URL I have developed a web app
code.gs
const doGet = _ => ContentService.createTextOutput(UrlFetchApp.fetch("https://docs.google.com/spreadsheets/d/e/2PACX-1vReY-tDEwKYjTiSjsfAN42qjFUwMv_OD3_64bFdGrgL-2p3otc13elLcCq3pkb5xqhTA-bW3QXobpqh/pub?gid=1861615717&single=true&range=c1:c5&output=csv").getContentText()).setMimeType(ContentService.MimeType.JAVASCRIPT);
In the web app output 1st line is ok,
but for 2nd, 3rd and 4th line there are Extra 2 Apostrophes coming at the starting and ending of the lines.
Here is My Web App
How to fix this ..?
Modification points:
In this case, how about directly retrieving the values from Spreadsheet using Spreadsheet service instead of UrlFetchApp.fetch("https://docs.google.com/spreadsheets/d/e/2PACX-1vReY-tDEwKYjTiSjsfAN42qjFUwMv_OD3_64bFdGrgL-2p3otc13elLcCq3pkb5xqhTA-bW3QXobpqh/pub?gid=1861615717&single=true&range=c1:c5&output=csv").getContentText()? I thought that the reason of your current issue might be due to exporting the Spreadsheet as CSV data. When the Spreadsheet service is used, the values can be retrieved.
In your previous question, I said "In this answer, your Spreadsheet is used. Of course, you can directly set the script in Web Apps.". Ref In your this question, I thought that this can be used.
In your sample Spreadsheet, document.getElementById("tdsyltt).innerHTML = tdsyltt ; is required to be document.getElementById("tdsyltt").innerHTML = tdsyltt ;. Please be careful about this.
When these points are reflected in your script, how about the following modification?
Modified script:
const doGet = _ => {
const spreadsheetId = "###"; // Please set your Spreadsheet ID.
const ss = SpreadsheetApp.openById(spreadsheetId);
const sheet = ss.getSheetByName("SYLLABUSC");
const html = sheet.getRange("C1:C6").getDisplayValues().filter(([c]) => c).join("\n");
// console.log(html); // When you directly run this function with the script editor, you can see the created value in the log.
return ContentService.createTextOutput(html).setMimeType(ContentService.MimeType.JAVASCRIPT);
}
From your Spreadsheet, in this sample script, I used "C1:C6" of "SYLLABUSC" sheet. So, please modify this for your actual situation.
Note:
When you modified the Google Apps Script of Web Apps, please modify the deployment as a new version. By this, the modified script is reflected in Web Apps. Please be careful about this.
You can see the detail of this in my report "Redeploying Web Apps without Changing URL of Web Apps for new IDE (Author: me)".
Try a way to solve this by changing the spreadsheet Publishing Format from csv to tsv then it will work.
Code.gs
const doGet = _ => ContentService.createTextOutput(UrlFetchApp.fetch("https://docs.google.com/spreadsheets/d/e/2PACX-1vReY-tDEwKYjTiSjsfAN42qjFUwMv_OD3_64bFdGrgL-2p3otc13elLcCq3pkb5xqhTA-bW3QXobpqh/pub?gid=1861615717&single=true&range=c1:c5&output=tsv").getContentText()).setMimeType(ContentService.MimeType.JAVASCRIPT);
I have several sheets that begin with "Agent Report" followed by the name, like this: "Agent Report - John", "Agent Report - Adam", etc. I have a script that reads the name of the agent from a specific cell and retrieves the data from another spreadsheet for that person. I want to trigger the script when a sheet that begins with "Agent Report" is activated, so that when I move between the sheets, the sheet data are updated for each person in "Agent Report" sheets. So far I have this:
function onOpen(e) {
makeMenu();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sname = ss.getActiveSheet().getName();
if(sname.indexOf("Agent Report")>-1){
master();
}
}
Agent Report is not the first sheet, so the script correctly makes a custom menu (makeMenu) when I open the spreadsheet, but does not get triggered (master) when I switch to an "Agent Report" sheet. When I run the script manually from an "Agent Report" sheet, it runs fine.
My question is: Can I create a trigger that will run the script when I switch to a sheet with the name that begins with "Agent Report"? onOpen() doesn't seem to fit for that purpose.
If such a trigger is not possible, can there be a workaround - a loop that would go over every sheet, check the name and if it contains "Agent Report", run the script. Something like that:
function onOpen(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var numberOfSheets = ss.getSheets().length;
for (var i = 0; i<=numberOfSheets; i ++) {
if(SOMETHING HERE.indexOf("Agent Report")[i] > -1){
master();
}
}
}
Issue:
According to the official documentation:
The onOpen(e) trigger runs automatically when a user opens a
spreadsheet, document, presentation, or form that they have permission
to edit.
The problem with this approach is that onOpen is executed only the time you open the spreadsheet file. Therefore, even if you switch between sheets or do any other operations, the script won't be executed, unless you refresh/open the spreadsheet file again. In other words, onOpen will only be executed for the sheet that you open the first time.
Solution 1:
To execute a code when switching between different sheets you can use onSelectionChange(e):
function onSelectionChange(e) {
makeMenu();
const as = e.source.getActiveSheet();
if (as.getName().indexOf("Agent Report")>-1){
master();
}
}
I don't recommend you to choose this approach because every time you make a selection change in the script, the code will be executed.
Solution 2 (recommended):
As you also proposed, to control when you want to execute the script I would advice you to use a regular function that iterates over all sheets and checks if the sheet name matches the criterion. Add also onOpen to execute makeMenu():
function allSheets(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
sheets.forEach(sh=>{
if(sh.getName().indexOf("Agent Report")>-1){
master();
}});
}
function onOpen() {
makeMenu();
}
You can execute this regular function in many different ways:
script editor
create a custom menu and execute it from the spreadsheet file
create an icon button within your spreadsheet file
create a time-driven trigger to execute on a particular time
Bonus information:
In both approaches, you can replace
if(as.getName().indexOf("Agent Report")>-1)
with
if(as.getName().includes("Agent Report"))
which does exactly the same thing, but it is more eye-friendly.
Related Issue:
As also pointed out by Iamblichus, an onSheetChange trigger function has already been requested by the community and it is reported in the IssueTracker. Go ahead and click on the star (★) button to the top left of the webpage to increase the chances of this feature to be implemented in the future.
The final solution was to use installable trigger (not a simple trigger, this is why the script did not run onOpen) with the following script:
function allSheets() {
makeMenu();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
sheets.forEach(sh=>{
SpreadsheetApp.setActiveSheet(sh);
if (sh.getName().includes("Agent Report")) {
master();
}
})
SpreadsheetApp.setActiveSheet(sheets[0]);
}
Thank you #Marios for all the helpful tips.
It seems like in the documentation that Google Spreadsheets and Google Sheets both have methods such as getActiveCell() which leads to confusion what the difference between the "Spreadsheet" object and the "Sheet" object. I assume that a spreadsheet is a collection of sheets. Suppose that I want to open a file with the following code where my Spreadsheet only has 1 sheet (see question in commented lines and assume I want to then do operations on the first sheet of both files):
function OpenFile(FileName) {
var FileToGetIterator = DriveApp.getFilesByName(FileName);
var FileToGet = FileToGetIterator.next();
var SpreadSheets = SpreadsheetApp.open(FileToGet);
var SpreadSheet1 = SpreadSheets.getSheets()[0]; // is this line necessary??
}
Similarly for creating a file
function CreateFile(SpreadSheetName) {
var NewSpreadSheets = SpreadsheetApp.Create(SpreadSheetName);
var NewSpreadSheet = NewSpreadSheets.getSheets()[0]; // is this line necessary?
}
when you create a spreadsheet or you open a spreadsheet it automatically open the first sheet of the spreadsheet,so no it's not necessary. nevertheless the opened sheet is opened as the "activesheet" and you've got less methods than if you specifically open a particular sheet with your method
I was interested in automating data validation of a Google Spreadsheet against another Google Spreadsheet using Google Scripts. I can't seem to find a way to reference the range of another spreadsheet using the openById method. Any thoughts?
function externalSheetDataValidation() {
var cell = SpreadsheetApp.getActiveRange();
//var dataValidationSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("dataValidationRules");
//var dataValidationSheet = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/10Z2s1bSRIhZzBBrMfPmhEphnPx-kJdV3LLlbv0L59g8/edit#gid=0");
var dataValidationSheet = SpreadsheetApp.openById("10Z2s1bSRIhZzBBrMfPmhEphnPx-kJdV3LLlbv0L59g8");
var sheet = dataValidationSheet.getSheets()[0];
var range = SpreadsheetApp.getActiveSheet().getRange("A3:A4");
var rule = SpreadsheetApp.newDataValidation()
.requireValueInRange(range, true)
.setAllowInvalid(false)
.build();
cell.setDataValidation(rule);
Logger.log(dataValidationSheet.getName());
}
If I am understanding correctly, the issue you are having is selecting a data range from an external spreadsheet?
Im assuming dataValidationSheet is supposed to be the external sheet. It looks like you were on the right track. It looks like the issue is that you are expecting the sheet to be active once you grab it.
Try this change:
var range = sheet.getRange("A3:A4");
Currently you are grabbing the external sheet, but then getting the range from the sheet that was already active. Hopefully this helps!
The code:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().openById('https://docs.google.com/a/e4b.us/spreadsheets/d/1DBty8ZiTqZ8MHPkFipjL8wDZqR2Ae9PLrNAQtW2QD2k/edit#gid=0');
var ss = SpreadsheetApp.getActive();
var mylocationInfo = ss.getRange("A2:B4").getValues();
logger.log(mylocationInfo.getData)
}
I thought that maybe removing non-essential aspects of the url, like the /edit#gid=0, would help. It didn't.
Here's evidence that the URL is indeed valid:
EDIT I've tried it without the entire URL, just using the actual ID and I receive the same message.
EDIT I removed the function ".getActiveSpreadSheet" and get the error "Bad Value"
I tried changing getById to getByUrl, inputing the full URL, and i get the error message that the file with the ID is missing. As was shown by the screenshot, the file exists. The url was copy-pasted immediately before executing the script.
changed:3:07pm
EDIT I tried linking to a different spreadsheet using getByUrl and got the same error message. Changed 3:10pm
Why does getById return Null?
Why can't getByUrl find my files?
You are using: getActiveSpreadsheet() and openById() at the same time. That's a conflict. You need to use one or the other.
Your code:
var sheet = SpreadsheetApp.getActiveSpreadsheet()
.openById('https://docs.google.com/a/e4b.us/spreadsheets/d/Your ID/edit#gid=0');
var ss = SpreadsheetApp.getActive();
Plus you aren't using the ID, you are using the URL. If you want to get a reference to the file by URL, use the method: openByUrl()
The code should look like this:
var ss = SpreadsheetApp.openById('Your ID');
var sheet = ss.getActiveSheet();
The variable name ss stands for spread sheet. That's the entire spreadsheet file. sheet is one sheet tab inside the spreadsheet.