Assign any kind of protection to a custom menu - google-apps-script

If it is not possible , can i make any trick to open a msgbox then end the script if anyone try to access the script
Here is my code :
function onOpen(){
var ui = SpreadsheetApp.getUi();
var menu = ui.createMenu('Private');
menu.addItem('Clear Cols','clearAli');
menu.addItem('Enter Number of days','noOfDays');
menu.addItem('subject','myEmail')
menu.addToUi();
} 
function clearAli(){
var ss = SpreadsheetApp.getActive();
ss.getRangeList(['G2:K108', 'M2:M108', 'O2:O108']).activate().clearContent();
}
function noOfDays(){
var
days =  SpreadsheetApp.getUi().prompt('Please enter the number of days.').getResponseText();
SpreadsheetApp.getActiveSpreadsheet().getRange('H2:H5').setValue(days);
}
function myEmail(){
const
subject = SpreadsheetApp.getUi().prompt('Enter your subject line').getResponseText();GmailApp.sendEmail('noelaramouny#gmail.com,alinemanoukian04#gmail.com',subject,'https://docs.google.com/spreadsheets/d/1Z92UC7RAqScObY2vpAKlzIvoY4yihxRtqw_2LEB2-8g/edit?usp=sharing');
}

const whitelist = ['mail1', 'mail2'];
const user = Session.getActiveUser().getEmail();
if (!whitelist.includes(user)) {
Browser.msgBox('Not in whitelist');
return;
}

Related

how to append sheet before next function run

I'm appending a row in a spreadsheet from a form then taking the data that was added to the spreadsheet and populating a document template. From there I'm creating a PDF and emailing it to myself. The problem I'm facing is that the data is always coming from the second to last row of the spreadsheet instead of the newly appended row (from the latest form data). It seems like the appended data is not being saved before the AutoFillDocFromTemplate function runs. What am I missing?
function doGet(request) {
return HtmlService.createTemplateFromFile('Index').evaluate();
};
/* #Include JavaScript and CSS Files */
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
/* #Process Form */
function processForm(formObject) {
var url = "https://docs.google.com/spreadsheets/d/1nz2uIWab1eSirljzvNn6SyNyxz3npDTu4mqVYV0blsU/edit#gid=0";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Data");
ws.appendRow([formObject.company_name,
formObject.identity_transformation,
formObject.character_want,
formObject.external_problem,
formObject.internal_problem,
formObject.philisophical_problem,
formObject.empathy,
formObject.authority,
formObject.plan_step1,
formObject.plan_step2,
formObject.plan_step3,
formObject.direct_cta,
formObject.transitional_cta,
formObject.failure,
formObject.success]);
}
/* This function creates a new document from a template and updates the placeholder with info from a Google Sheet*/
function AutofillDocFromTemplate(){
// Get the spreadsheet & sheet
var url = "https://docs.google.com/spreadsheets/d/1nz2uIWab1eSirljzvNn6SyNyxz3npDTu4mqVYV0blsU/edit#gid=0";
var ss = SpreadsheetApp.openByUrl(url).getSheetByName("Data");
// Set the range to the last row of data in the Sheet
var data = ss.getRange(ss.getLastRow(),1,1, ss.getLastColumn()).getValues();
// Get the original template Doc
const templateDoc = DriveApp.getFileById("1yu5jzg4NbRtTy_UjwzBmnpc-3_pNOqA-l1_UVsiAIWQ");
// Get the folder for where the docs should go
const folder = DriveApp.getFolderById("1prOQxp5jmDvJqiwIfLbbkLYWoz5QlTUC");
// Create the new file name
const newFileName = ("BrandScript")
// Create a copy of the template doc
const newTempFile = templateDoc.makeCopy(newFileName, folder);
// Open the new temp doc
const openDoc = DocumentApp.openById(newTempFile.getId());
// Get the body of the new temp doc
const body = openDoc.getBody();
// Replace placeholders with spreadsheet data from last row
body.replaceText("%company_name%", data[0][0]);
body.replaceText("%identity_transformation%", data[0][1]);
body.replaceText("%character_want%", data[0][2]);
body.replaceText("%external_problem%", data[0][3]);
body.replaceText("%internal_problem%", data[0][4]);
body.replaceText("%philisophical_problem%", data[0][5]);
body.replaceText("%empathy%", data[0][6]);
body.replaceText("%authority%", data[0][7]);
body.replaceText("%plan_step1%", data[0][8]);
body.replaceText("%plan_step2%", data[0][9]);
body.replaceText("%plan_step3%", data[0][10]);
body.replaceText("%direct_cta%", data[0][11]);
body.replaceText("%transitional_cta%", data[0][12]);
body.replaceText("%failure%", data[0][13]);
body.replaceText("%success%", data[0][14]);
// Save and close the new doc
openDoc.saveAndClose();
//Send email with new document
var message = "Attached is your draft BrandScript"; // Customize message
var emailTo = "to be inserted" // replace with your email
var subject = "Your Draft BrandScript"; // customize subject
var pdf = DriveApp.getFileById(openDoc.getId()).getAs('application/pdf').getBytes();
var attach = {fileName:'DraftBrandScript.pdf',content:pdf, mimeType:'application/pdf'}; // customize file name: "Autogenerated template"
MailApp.sendEmail(emailTo, subject, message, {attachments:[attach]});
}
<script>
// Prevent forms from submitting.
function preventFormSubmit() {
var forms = document.querySelectorAll('form');
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener('submit', function(event) {
event.preventDefault();
});
}
}
window.addEventListener('load', preventFormSubmit);
function handleFormSubmit(formObject) {
google.script.run.processForm(formObject);
google.script.run.AutofillDocFromTemplate();
document.getElementById("myForm").reset();
}
</script>
I think that the reason of your issue is google.script.run works with the asynchronous process. By this, at the following script,
google.script.run.processForm(formObject);
google.script.run.AutofillDocFromTemplate();
Before processForm is not finished, AutofillDocFromTemplate is run. So in order to remove your issue, I would like to propose the following patterns.
Pattern 1:
In this pattern, withSuccessHandler is used. By this, AutofillDocFromTemplate is run after processForm was run.
From:
google.script.run.processForm(formObject);
google.script.run.AutofillDocFromTemplate();
To:
google.script.run.withSuccessHandler(() => google.script.run.AutofillDocFromTemplate()).processForm(formObject);
Pattern 2:
In this pattern, Google Apps Script is modified. By this, AutofillDocFromTemplate is run after processForm was run.
Google Apps Script side:
From:
function processForm(formObject) {
var url = "https://docs.google.com/spreadsheets/d/1nz2uIWab1eSirljzvNn6SyNyxz3npDTu4mqVYV0blsU/edit#gid=0";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Data");
ws.appendRow([formObject.company_name,
formObject.identity_transformation,
formObject.character_want,
formObject.external_problem,
formObject.internal_problem,
formObject.philisophical_problem,
formObject.empathy,
formObject.authority,
formObject.plan_step1,
formObject.plan_step2,
formObject.plan_step3,
formObject.direct_cta,
formObject.transitional_cta,
formObject.failure,
formObject.success]);
AutofillDocFromTemplate() // <--- Added
}
HTML&Javascript side:
google.script.run.processForm(formObject);
// google.script.run.AutofillDocFromTemplate(); // <--- removed
Note:
If the issue was not resolved by above modifications, please try to use SpreadsheetApp.flush().
Reference:
Class google.script.run

How can i create a button in a cell using only google app script? not with the Insert>drawing option

I need to do a button with a script its required for the thing i want to do so the button that can be created in insert>draw option doesn't have what i need, i want to put a entire call with buttons in each row son any button execute a function when is clicked, I want to know if this is possible
I looked about this information here, but really dont know how to do it
Using one script for copying text from cells in a row to another sheet using an icon in a row
Script in my html that embed my trello cards
<script>
(() => {
google.script.run.withSuccessHandler(myname => {
const tag = document.createElement('script');
tag.src = "https://p.trellocdn.com/embed.min.js";
tag.addEventListener('load', () => {
const elem = document.querySelector('#card');
elem.href = myname;
window.TrelloCards.load(document); // <-- this triggers the lookup
});
document.body.append(tag);
}).getCardById();
})();
</script>
Function getCardById
function getCardById(){
var app = SpreadsheetApp;
var activeSp = app.getActiveSpreadsheet();
var activeSheet = activeSp.getActiveSheet();
//Range of links
var linkRange = activeSheet.getRange(2,2,3,1);
//Cuantity of values
var filLength = linkRange.getValues();
var cont = 2;
if (activeSheet.getActiveCell().getColumn() == 6.0){
for (i = 0; i < filLength.length ; i++){
if (activeSheet.getActiveCell().getRow() == activeSheet.getRange(cont,2).getRow()){
var linkCol = 2.0;
var linkFil = activeSheet.getActiveCell().getRow();
var linkRange = activeSheet.getRange(linkFil, linkCol);
var linkValue = linkRange.getValue();
var url = linkValue;
return url;
}
cont++;
}
}
}
Function seeCard its the one that should be displayed when pressing the checkbox, when you press it should show you values of the same row
function seeCard(){
var app = SpreadsheetApp;
var activeSp = app.getActiveSpreadsheet();
var activeSheet = activeSp.getActiveSheet();
var startCol = 6;
var startFil = 2;
var numCols = 1;
var numFils = 2;
//Rango de links
var linkRange = activeSheet.getRange(2,2,3,1);
//Cantidad de valores para el for
var filLength = linkRange.getValues();
var cont = 2;
if (activeSheet.getActiveCell().getColumn() == 6.0){
for (i = 0; i < filLength.length ; i++){
if (activeSheet.getActiveCell().getRow() == activeSheet.getRange(cont,2).getRow()){
//Html
var templateFileId = '1upA3JHioEyxLScebasmsmwXW-SxsiKaPRznKLCKFYhw';
var sheetName = 'Trello sheet';
SpreadsheetApp.openById(templateFileId).copy(sheetName);
var excel = SpreadsheetApp.create("Probando un html interno a ver ")
var html = HtmlService.createHtmlOutputFromFile('index') //index is the name of your HTML file
.setTitle('Trello card')
.setWidth(350)
.setHeight(250);
SpreadsheetApp.getUi().showModalDialog(html, 'trello card '); // Or DocumentApp or FormApp.
//End
}
cont++;
}
}
}
Where is the button that says seeCard i need to put the checkbox and when you click grab the link send it to my html and show the trello card attached to that link
You want to put the checkboxes to the cells of "F2:F".
You want to run seeCard() when the checkbox is checked.
I could understand like above. If my understanding is correct, how about this answer?
Sample script:
Please copy and paste the following script. And, please install OnEdit event trigger to the function of installedOnEdit. Because in your script, when a simple trigger is used, an error occurs at the methods which are required to authorize like SpreadsheetApp.openById() and SpreadsheetApp.create(). So please use the installable OnEdit event trigger for this.
function installedOnEdit(e) {
const range = e.range;
if (range.columnStart == 6 && range.rowStart > 1 && range.isChecked()) {
range.uncheck();
seeCard();
}
}
In order to run the script, please check a checkbox at the column "F". By this, the function of installedOnEdit is run by the installable OnEdit event trigger. In your script, the active range is retrieved from your script. So in your case, I think that this sample script works.
Note:
I thought that the event object can be also used. But I'm not sure about your whole script. So I couldn't propose it.
References:
Installable Triggers
isChecked()
function onEdit(e) {
//e.source.toast('Entry');
const sh=e.range.getSheet();
if(sh.getName()=='Sheet13') {
if(e.range.columnStart==1 && e.range.rowStart==1 ) {
e.range.setValue('FALSE');
one(e);
}
if(e.range.columnStart==2 && e.range.rowStart==1 ) {
e.range.setValue('FALSE')
two(e);
}
}
}
function one(e) {
e.source.toast('This is function one');
}
function two(e) {
e.source.toast('This is function two');
}
Image:
Animation:

Password Protect Google Apps Script

so I have created a small script here for my google sheets. Since google sheets doesn't allow you to use password protection on individual sheets, I was wondering if there was a way to protect my script with a password so that only certain people can use it. Here is my code.
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Custom Menu')
.addItem('Record', 'Record')
.addItem('Cancelation', 'Cancel')
.addToUi();
}
function Record() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Nightly Stats'),
row = sheet.getLastRow()
range = sheet.getRange("A3:G3");
sheet.insertRowAfter(row);
range.copyTo(sheet.getRange(row + 1, 1), {contentsOnly:true});
}
I would greatly appreciate any suggestions that you can provide.
Ok so I actually figured out how to do a password system via prompting. Here was what I did in case anyone needs this in the future.
function Cancel() {
var SS = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
// first prompt
var presult = ui.prompt(
"Please Enter the Password to Use this Feature.",
ui.ButtonSet.OK_CANCEL);
var password = "Test";
var pbutton = presult.getSelectedButton();
var ptext = presult.getResponseText();
// User clicked "OK" on first prompt
if (pbutton == ui.Button.CANCEL) {
ui.alert('The Process Was Ended.');
} else if (pbutton == ui.Button.CLOSE) {
ui.alert('The Process Was Ended.');
} else if (ptext != password) {
Password();
} else {
"Insert whatever action you would want them to do after the password works here"
}
}
function Password() {
var SS = SpreadsheetApp.getActiveSpreadsheet();
var ui = SpreadsheetApp.getUi();
var response = ui.alert("The Password is Incorrect. Retry?",
ui.ButtonSet.OK_CANCEL);
if (response == ui.Button.CANCEL) {
ui.alert("The Process Was Ended.");
} else if (response == ui.Button.CLOSE) {
ui.alert("The Process Was ended.");
} else {
Cancel();
}
}
I only gave a piece of the code so sorry if it looks a little weird. I just didn't want to give the whole code and make you search for everything. Hope that helps :)
It is a trick but it's useful to free account user!
Use onOpen() function and little code below.
var inputPassword;
function checkPassword(){
var refAddress = 'https://script.google.com/d/' + ScriptApp.getScriptId() + '/edit';
var curAddress = refAddress;
Logger.log(curAddress);
var referPassword = 1234;
if( refAddress == curAddress){
while(referPassword != inputPassword){
inputPassword = Browser.inputBox('[Password Check]', '[Input Password!]', Browser.Buttons.OK_CANCEL);
}
}
}
Refer to my blog for more information.
So you're trying to protect the script itself?
Host the script in a different location and use a library:
https://developers.google.com/apps-script/guides/libraries

Google Apps Script : reference error getValue is not defined

I'm working on a Google App Script to use it in a spreadsheet:
I just want to get the value in the ID tab, cell B2
function onInstall(e) {
onOpen(e);
}
function onOpen(e) {
var menu = SpreadsheetApp.getUi().createAddonMenu();
menu.addItem('Générer RM', 'createRM');
menu.addToUi();
}
var responsableProjet = {};
function createRM() {
getAdminInfo();
}
function getAdminInfo() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
responsableProjet.sexe = getValue(sheet, 'ID!B2');
}
I'm having this weird error..
Thanks for your help ! :)
You have to define a range then, you can call "getValue" : https://developers.google.com/apps-script/reference/spreadsheet/sheet#getrangea1notation + https://developers.google.com/apps-script/reference/spreadsheet/range#getvalue
The code could be something like :
responsableProjet.sexe = sheet.getRange('ID!B2').getValue();
to test...

How to use global variables in event handlers

I have a global variable contacts,
var contacts = ContactsApp.getContacts();
I have an KeyUphandler for my textbox called search().
In search(),
contacts[0].getPrimaryEmail() gives out an error, "Cant call method getPrimaryEmail of undefined"
However contacts[0].getPrimaryEmail() works fine in other normal functions.
Cant we use global variables in event handlers?
The code is given below,
//CODE starts
var contacts = ContactsApp.getContacts();
function ShowAuto() {
var app = UiApp.createApplication().setTitle('Simple Autocomplete');
var Panel = app.createVerticalPanel().setId('mainPanel').setWidth('555px');
var textBox = app.createTextBox().setName('textBox').setWidth('330px').setId('textBox');
var tBoxHandler = app.createServerKeyHandler('search');
tBoxHandler.addCallbackElement(textBox);
textBox.addKeyUpHandler(tBoxHandler);
var listBox = app.createListBox().setName('list').setWidth("330px").setId('list').setVisibleItemCount(5)
.setStyleAttribute("border", "1px solid white")
.setStyleAttribute("background", "white").setVisible(false);
Panel.add(app.createLabel().setId('label'));
Panel.add(textBox);
Panel.add(listBox);
app.add(Panel);
var ownerEmail = Session.getActiveUser().getEmail();
// I used this method first.. It din work.
// var contacts = ContactsApp.getContacts();
// for (var i in contacts)
// {
// var emailStr = contacts[i].getPrimaryEmail();
// conts[i]=emailStr;
// Browser.msgBox(emailStr);
// }
//These two calls are just to test, the contacts Array, They work fine :)
getContact(0);
getContact(1);
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.show(app);
}
function getContact(n)
{
// Browser.msgBox(contacts[n].getPrimaryEmail());
Logger.log(contacts[n].getPrimaryEmail()); //works fine
return contacts[n].getPrimaryEmail();
}
function search(e)
{
var app = UiApp.getActiveApplication();
//Logger.log(contacts[0].getPrimaryEmail()); //Not working
app.getElementById('label').setText(e.parameter.textBox.toString());
// app.getElementById('label').setText(conts[0]);
app.getElementById('list').setVisible(true);
var searchKey = new RegExp(e.parameter.textBox.toString(),"gi");
if (searchKey == "")
app.getElementById('textBox').setValue('');
var listBoxCount = 0;
for (i=0;i<5;i++){
//Here is where i get error
if(contacts[i].indexOf(e.parameter.textBox)!=-1 && listBoxCount < 5)
{
app.getElementById('list').addItem(conts[i]);
listBoxCount++;
}
}
}
Sorry if i was unclear
Well, I have used UserProperties.setProperty and getProperty function to avoid the use of global variables.
Whenever a server handler is invoked, the global variables will be recalculated as given in https://developers.google.com/apps-script/class_serverhandler, so mine was not working properly.
Thanks for the support guys :)