I have been trying to lookup username using activeuser. Only the first part works and the last part doesnt. My goal is to unhide the sheet based on the username of the active user (sheetname is based on username). Below is the code I am using.
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [];
menuEntries.push({name: "Test getActiveUser/getEmail", functionName: "onTest"});
ss.addMenu("Rep Drowndown", menuEntries);
testGetEmail("onOpen");
}
function onTest() {
testGetEmail("menu function");
};
function testGetEmail(callerId) {
var userEmail = "";
var activeUser = Session.getActiveUser();
if (activeUser == null)
Browser.msgBox("Session.getActiveUser() returned null", "called by " + callerId, Browser.Buttons.OK);
else
userEmail = activeUser.getEmail();
if (userEmail == "")
Browser.msgBox("Your Email returned an empty string", "called by " + callerId, Browser.Buttons.OK);
else
var ss = SpreadsheetApp.getActiveSpreadsheet();
var lookup = Session.getActiveUser().getEmail();
var range = ss.getRange('$A$3:$B$8').getValues();
var lookupRange = [];
for (var i = 0; i < range.length; i++)
lookupRange.push(range[i][0]);
var index = lookupRange.indexOf(lookup);
if (index == -1) {
// implicit no-op
}
else {
var link = range[index][2]
var sheet = ss.getSheetByName(link);
sheet.showSheet();
};
}
You need to set up an installable trigger. This allows the script to run with authorization, whereas normally "onOpen()" is run as a Simple Trigger. Since Simple Triggers can't authorize as users, you'll never get an email address.
The simplest solution is to set testGetEmail() to be run on open. Do this within the Script Editor by choosing Resources > Current Project's Triggers in the menu. Then click "No triggers set up. Click here to add one now." Finally, set up your trigger:
Choose your function's name (testGetEmail) from the first dropdown under "Run."
Choose "From spreadsheet" in the second dropdown under "Events."
Choose "On Open" in the third dropdown.
Then test to be sure I didn't commit a typo :-)
Related
WARNING: I'm not a programmer by trade.
Ok. Got the disclaimer out of the way. So this might not be the best way to do this but here is the scenario. I have a dropdown that gets populated via a Google Sheet. The user chooses a selection from the list but this dropdown does not have all of the possible values it could have. There will likely be a time when the user needs a new value added. While I could manually update the spreadsheet as new values are requested that introduces an element of human availability to get this done and I'm not always available.
What I would prefer is a self-serve model. I want to supply the user with a text field where they can enter the new value and submit it to the Google Sheet. Then I would like the dropdown to be updated with the new value for the user to choose.
Now, I realize that I could just submit the value in the new field to the Google Sheet but that will require building a condition to see whether it is the dropdown or text field that has a value in it. I'd also need some type of error handling in case both the dropdown and text field have values. That seems like a bigger headache to program then my ask.
I'm not sure what code you would need to see to help make this work but here is what I think might help.
doGet function
function doGet(e){
var ss = SpreadsheetApp.openById(ssId)
var ws = ss.getSheetByName("External");
var range = ws.getRange("A2:D2");
var valuesArray = [];
for (var i = 1; i <= range.getLastColumn(); i++){
var lastRowInColumn = range.getCell(1, i).getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow();
var list = ws.getRange(2,i,lastRowInColumn-1,1).getValues();
valuesArray.push(list);
}
var userEmail = Session.getActiveUser().getEmail();
var sourceListArray = valuesArray[2].map(function(r){ return '<option>' + r[0] + '</option>'; }).join('');
var productListArray = valuesArray[3].map(function(r){ return '<option>' + r[0] + '</option>'; }).join('');
var tmp = HtmlService.createTemplateFromFile("config");
tmp.productList = productListArray;
return tmp.evaluate();
}
Add to Google Sheet
function userClicked(tagInfo){
var ss = SpreadsheetApp.openById(ssId)
var ws = ss.getSheetByName("Data");
ws.appendRow([tagInfo.email, tagInfo.source, tagInfo.product, new Date()]);
}
Add record
function addRecord(){
var tagInfo = {};
tagInfo.product = document.getElementById("product").value;
google.script.run.userClicked(tagInfo);
var myApp = document.getElementById("source");
myApp.selectedIndex = 0;
M.FormSelect.init(myApp);
var myApp = document.getElementById("brand");
myApp.selectedIndex = 0;
M.FormSelect.init(myApp);
var myApp = document.getElementById("product");
myApp.selectedIndex = 0;
M.FormSelect.init(myApp);
}
How dropdowns are populated in the HTML.
<div class="input-field col s3">
<select id="product" onchange="buildURL()">
<option disabled selected value="">Choose a product</option>
<?!= productList; ?>
</select>
<label>Product</label>
</div>
Need to see anything else? I think it might be relatively easy to add the new value to the column but the tricky part seems to be the update of only that one dropdown and not the entire app. To me it seems like I want to trigger the doGet() function again but only for that specific dropdown. Thoughts?
UPDATE: current code to add new value to dropdown
function addProduct() {
let newProd = document.getElementById("newProduct").value;
google.script.run.withSuccessHandler(updateProductDropdown).addNewProduct(newProd);
document.getElementById("newProduct").value = "";
}
function updateProductDropdown(newProd){
var newOption = document.createElement('option');
newOption.value = newProd;
newOption.text = newProd;
document.getElementById('product').add(newOption);
}
UPDATE2: App Scripts function to add new value to column in spreadsheet
function addNewProduct(newProd){
var columnLetterToGet, columnNumberToGet, direction, lastRow, lastRowInThisColWithData, rng, rowToSet, startOfSearch, valuesToSet;
var ss = SpreadsheetApp.openById(ssId);
var ws = ss.getSheetByName("List Source - External");
lastRow = ws.getLastRow();
//Logger.log('lastRow: ' + lastRow)
columnNumberToGet = 9;//Edit this and enter the column number
columnLetterToGet = "I";//Edit this and enter the column letter to get
startOfSearch = columnLetterToGet + (lastRow).toString();//Edit and replace with column letter to get
//Logger.log('startOfSearch: ' + startOfSearch)
rng = ws.getRange(startOfSearch);
direction = rng.getNextDataCell(SpreadsheetApp.Direction.UP);//This starts
//the search at the bottom of the sheet and goes up until it finds the
//first cell with a value in it
//Logger.log('Last Cell: ' + direction.getA1Notation())
lastRowInThisColWithData = direction.getRow();
//Logger.log('lastRowInThisColWithData: ' + lastRowInThisColWithData)
rowToSet = lastRowInThisColWithData + 1;
valuesToSet = [newProd];
ws.getRange(rowToSet, 9).setValues([valuesToSet]);
return newProd;
}
SOLUTION to Update Materialize Dropdown
function updateProductDropdown(newProd){
newProdOption = document.getElementById('product');
newProdOption.innerHTML += '<option>' + newProd + '</option>';
var elems = document.querySelectorAll('select');
var instances = M.FormSelect.init(elems);
}
You can specify a client side callback function if you use google.script.run withSuccessHandler(callback) where your callback could update the list only and not the whole site.
Example:
google.script.run.withSuccessHandler(updateDropdownWidget).updateDropdownList(text_from_input)
Where updateDrownList(text_from_input) is a function in your Apps Script that adds text to the sheet using SpreadsheetApp for example, and returns the "text" to the callback function: updateDropdownWidget(text) which adds a new list item to the HTML drop-down list in your front end.
index.html:
<form>
<label for="newOption">New option for the dropdown:</label>
<input type="text" id="nopt" name="newOption">
<input type="button" value="Submit"
onclick="google.script.run.withSuccessHandler(updateDropdownWidget)
.updateDropdownList(document.getElementById('nopt').value)">
</form>
<label for="cars">Choose a car:</label>
<select name="cars" id="cars">
<?!= values; ?>
</select>
<script>
function updateDropdownWidget(text){
var option = document.createElement('option');
option.value = text;
option.text = text;
document.getElementById('cars').add(option);
}
</script>
Code.gs:
function doGet(e){
var ss = SpreadsheetApp.getActiveSheet();
var lastRow = ss.getDataRange().getLastRow();
var values = ss.getRange(1,1,lastRow,1).getValues();
var valuesArray = [];
for (var i = 0; i < values.length; i++){
valuesArray.push('<option value="'+values[i]+'">' +values[i]+ '</option>');
}
var tmp = HtmlService.createTemplateFromFile("index");
tmp.values = valuesArray;
return tmp.evaluate();
}
function updateDropdownList(text_from_input){
// Log the user input to the console
console.log(text_from_input);
// Write it to the sheet below the rest of the options
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getDataRange().getLastRow();
sheet.getRange(lastRow+1,1).setValue(text_from_input);
// Return the value to the callback
return text_from_input;
}
Here's an example:
In my Stack Over Flow spreadsheet I four buttons which can be used to run any function in 3 script files and every time I load the sidebar it reads the functions in those script files and returns them to each of the select boxes next to each button so that I test functions that I write for SO with a single click and I can select any function for any button. Here's the Javascript:
$(function(){//JQuery readystate function
google.script.run
.withSuccessHandler(function(vA){
let idA=["func1","func2","func3","func4"];
idA.forEach(function(id){
updateSelect(vA,id);
});
})
.getProjectFunctionNames();
})
Here is GS:
function getProjectFunctionNames() {
const vfilesA=["ag1","ag2","ag3"];
const scriptId="script id";
const url = "https://script.googleapis.com/v1/projects/" + scriptId + "/content?fields=files(functionSet%2Cname)";
const options = {"method":"get","headers": {"Authorization": "Bearer " + ScriptApp.getOAuthToken()}};
const res = UrlFetchApp.fetch(url, options);
let html=res.getContentText();
//SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html), "Project Functions");
let data=JSON.parse(res.getContentText());
let funcList=[];
let files=data.files;
files.forEach(function(Obj){
if(vfilesA.indexOf(Obj.name)!=-1) {
if(Obj.functionSet.values) {
Obj.functionSet.values.forEach(function(fObj){
funcList.push(fObj.name);
});
}
}
});
//SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(funcList.join(', ')), "Project Functions");
return funcList;//returns to withSuccessHandler
}
Image:
Animation:
I am using a google form to send out emails to parents and coaches if students are missing work. The problem I am running into is that the sometimes the coach email is not listed because the student is not in a sport, so it has a blank cell and then the script will stop running. I just want it to skip over and continue moving down the sheet. Also is there a way that I can get a cell to confirm that it sent? like having the cell next to the email say "sent" or "error" if it fails? I'm not really concerned with this part it just would be nice. I'm really new to this. Thanks!
function SendCoachEmails() {
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Homework Hour 4.0").activate();//take off 4.0 when done with texting
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lr = ss.getLastRow();
var templateText = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DO NOT DELETE! Parent Email Template").getRange(1, 1).getValue();
var quotaLeft = MailApp.getRemainingDailyQuota();
if((lr-1) > quotaLeft) {
Browser.msgBox("You have " + quotaLeft + " left and you're trying to send" (lr-1) + " emails. Emails were not sent")
} else {
for (var i = 2;i<=lr;i++){ // i is the current row so in this i is row 2
var currentEmail = ss.getRange(i,12).getValue(); // 15 is the email column
var currentStudent = ss.getRange(i, 4).getValue(); // 4 is student name column
var currentMessage = ss.getRange(i, 2).getValue(); // 2 is the message column
var currentMissingAssingment = ss.getRange(i, 9).getValue(); // 9 is the missing assingment column
var massageBody = templateText.replace("<<Student Name>>",currentStudent).replace("<<Message>>",currentMessage).replace("<<Missing Assingment>>",currentMissingAssingment);
var subjectLine = currentStudent +" Has Homework Hour"; // change whats in the "" to what you need the subject on the email to be
MailApp.sendEmail(currentEmail, currentStudent, massageBody); // to change subject on email change "Has Homework Hour" Part//
}
}
}
You can use a simple condition statement, i.e. check if there is an email before you send
if (currentEmail.trim() !== '')// check if email is not blank
MailApp.sendEmail(currentEmail, currentStudent, massageBody); // to change subject on email change "Has Homework Hour" Part//
}
I'm totally lost here!
My spreadsheet has one sheet " Product Master Record" with a table containing three columns: Product ID (numerical) | Product Description | Product Status.
Each product has its own sheet within the spreadsheet.
The name of each sheet depends on the product status:
- "Active" product: sheet name = product name --> Sheet is visible
- "Inactive" product: sheet name = product ID --> Sheet is hidden
So I'm trying to write a macro which hide/shows sheets depending on the condition:
Product status (active/inactive)
Sheet name (if name contains text or not)
I don't need to write both conditions; only the easiest one to code.
Any ideas??
Thank you!
Here the sample for your purpose, ProdMaster, row 1 header [ProdId,Desc,Status], and rows afterward are data, and Status will be Inactive and Active:
function HideSheets() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheetByName('ProdMaster');
var lrow = sheet.getLastRow();
var myValue = sheet.getRange("A2:C" + lrow).getValues();
for (a=1; a<myValue.length+1; a++)
{
var target = myValue[a-1];
var tsheet = spreadsheet.getSheetByName(target[0]);
if(tsheet==null) spreadsheet.insertSheet(target[0]);
if(target[2]=='Active') tsheet.showSheet();
if(target[2]=='Inactive') tsheet.hideSheet();
}
};
As a way for solving your problem you could do the following, instead of using "Active" or "Inactive" as checkers for validating a boolean condition, in your "Product Master Record" you could set the status using the functions TRUE and FALSE, in that way you could decouple hardcoded text from your code and avoid the trouble from someone misspelling the words "Active" or "Inactive".
function HideSheets() {
// Get the active sheet and range of rows you want
var spreadSheet = SpreadsheetApp.getActive();
var mainSheet = spreadSheet.getSheetByName('Product Master Record');
var rows = mainSheet.getRange("A2:C" + mainSheet.getLastRow()).getValues();
// Iterate over all rows
for(row in rows){
// Get the sheets from their names and check if not null
var secondarySheet = spreadSheet.getSheetByName(rows[row][0]);
if(secondarySheet){
if(rows[row][2]) secondarySheet.showSheet(); // show the sheet if value is true
if(!rows[row][2]) secondarySheet.hideSheet(); // hide sheet if value is false
}
}
};
Docs
I can see your new in this wonderful world. I will give you some docs to help you in your path of learning:
Google Apps Script Reference
Overview of Google Apps Script
Class Sheet
Thank you all!
Finally I found help with a colleague and it works. This is the code in case someone finds useful:
function updateSheetFromIndex(index)
{
var app = SpreadsheetApp;
var ss = app.getActiveSpreadsheet();
var activeSheet = ss.getSheetByName(sheetNameOfTheMenu);
var cell = activeSheet.getRange(index, 5);
var formula = cell.getFormula();
var regExpr = /\#gid=([0-9]*)"/;
var result = regExpr.exec(formula);
var cellStatus = activeSheet.getRange(index, 4);
Logger.log(index)
if (result != null) {
var gid = result[1];
for each (var sheet in SpreadsheetApp.getActive().getSheets()) {
if(sheet.getSheetId()==gid){
if (cellStatus.getValue() == "Activo") {
Logger.log("Activo---");
sheet.showSheet();
}
if (cellStatus.getValue() == "Inactivo") {
Logger.log("Inactivo---");
sheet.hideSheet();
}
break;
}
}
} else {
Logger.log("+++ No pattern matches");
Logger.log("Formula");
Logger.log(formula);
}
}
I am wondering how to take the information from a parsed query string and use it to display on the top of my page. Ignore the window.alert part of the code, I was just using that to verify that the function worked.
For example: If the user had choices of Spring, Summer, Winter, and Fall, whichever they chose would display a a header on the next page. So if (seasonArray[i]) = Fall, I want to transfer that information into the form and display it as a element. I'm sure this is easily done, but I can't figure it out. Thanks, in advance.
function seasonDisplay() {
var seasonVariable = location.search;
seasonVariable = seasonVariable.substring(1, seasonVariable.length);
while (seasonVariable.indexOf("+") != -1) {
seasonVariable = seasonVariable.replace("+", " ");
}
seasonVariable = unescape(seasonVariable);
var seasonArray = seasonVariable.split("&");
for (var i = 0; i < seasonArray.length; ++i) {
window.alert(seasonArray[i]);
}
if (window != top)
top.location.href = location.href
}
<h1 id="DynamicHeader"></h1>
Replace the alert line with:
document.getElementById("DynamicHeader").insertAdjacentHTML('beforeend',seasonArray[i]);
In NetSuite I have a custom record for keeping track of our safety meetings, from the record, I have a user-event script, BEFORE SUBMIT FUNCTION, running to create an event record. On the Event record -> attendee sublist, I am able to add the attendees, but I am unable to set the sendemail checkbox. Any insight would be appreciated.
/*
user event script
before record submit
creates a new event record based off this safety meeting record.
*/
function createSafetyMeetingEventRec(type){
if(type=="create")
{
try
{
//get values from the safety meeting record
var altName = nlapiGetFieldValue('altname');
var message = nlapiGetFieldValue('custrecord53');
var local = nlapiGetFieldValue('custrecord56');
var date = nlapiGetFieldValue('custrecord51');
var time = nlapiGetFieldValue('custrecord52');
//name of the event record
var eventTitle = 'SM-' + altName;
//create the event record
var eventRec = nlapiCreateRecord('calendarevent');
//set the event record field values
eventRec.setFieldValue('title', eventTitle);
//script search for the Safety Committee group members in netsuite
var entitygroupSearch = nlapiSearchRecord("entitygroup",null,
[
["internalid","anyof","120147"]
],
[
new nlobjSearchColumn("entityid","groupMember",null),
new nlobjSearchColumn("internalid","groupMember",null)
]
);
//get who created the event, this user is automatically on the attendee list, and cannot be added again.
var eventUserSet = eventRec.getLineItemValue('attendee', 'attendee', 1);
for(var i = 0; i < entitygroupSearch.length; i++){
var newAt = eventRec.getLineItemCount('attendee') + 1;
var intIDuser = entitygroupSearch[i].getValue("internalid","groupMember",null);
if(intIDuser != eventUserSet){
eventRec.setLineItemValue('attendee', 'sendemail', newAt, 'T');
eventRec.setLineItemValue('attendee', 'attendee', newAt, intIDuser);
}else{
continue;
}
}
//set the resource calendar to Service Calendar, 3 is the internal id of the service calendar resource
var newAtResource = eventRec.getLineItemCount('resource') + 1;
eventRec.setLineItemValue('resource', 'resource', newAtResource, '3');
var eventId = nlapiSubmitRecord(eventRec, true);
}catch(err)
{
nlapiLogExecution("error","Error Creating Event Record From Safety Record ","Details: " + err.message);
}
}//end if
}
I think you also need
eventRec.setFieldValue('sendemail', 'T');
before the submit