Related
I found the following code online but can't remember where I got it from. The script will search for any emails with a specific label (in this case "GReminder") and add those emails into your calendar. I tried to set this up but I'm getting the error message:
TypeError: label.getThreads is not a function
I'm not familiar with Javascript and I'm not a software developer by any means. I've tried to modify it but couldn't get it to work. Hoping someone can help me here. Here is the code below:
function gMailReminder() {
var reminderLabel = "GReminder", //Substitute your label here
calendarName = "Mobile Calendar", ////Substitute your Calendar name here
reminderDuration = 2, //duration in hours
label = GmailApp.getUserLabelByName(reminderLabel),
threads = label.getThreads();
if (threads.length > 0) {
//get calendar by name
var cals = CalendarApp.getCalendarsByName(calendarName);
var now = new Date().getTime();
for (i in threads) {
cals[0].createEvent(reminderLabel + '- '+threads[0].getFirstMessageSubject(),
new Date(now+(60000*60*reminderDuration)),
new Date(now+(60000*60*reminderDuration)), {description: threads[i].getPermalink()});
}
//Remove the label from the mails to avoid duplicate event creation on next run
label.removeFromThreads(threads);
}
}
Furthermore, the code searches for the label "GReminder" and then removes that label at the end of the script. I'd like to modify this so that the script searches for GReminder in Gmail, then adds a new GReminder-Processed label to it (to show that those emails have been "processed") and then the next time it runs again, it will skip emails with GReminder-Processed. I've only gotten the part of creating the labels sorted (I think), but can't figure out the rest...
function getOrCreateLabel(labelName) {
var label = GmailApp.getUserLabelByName(labelName);
if (label == null) {
label = GmailApp.createLabel(labelName);
}
return label;
}
var label = getOrCreateLabel("Bill Reminder - processed");
thread.addLabel(label);
function gMailReminder() {
const rlbl = "GReminder";
const rdur = 2;
const label = GmailApp.getUserLabelByName(rlbl);
const threads = label.getThreads();
const cal = CalendarApp.getCalendarsByName("Mobile Calendar")[0];
if (threads.length > 0) {
threads.forEach(t => {
let now = new Date().getTime();
cal.createEvent(rlbl + '- ' + t.getFirstMessageSubject(), new Date(now), new Date(now + (60000 * 60 * rdur)), { description: t.getPermalink() });
});
label.removeFromThreads(threads);
}
}
I think probably your label name is wrong:
Here's a function to get your label names:
function getUserLabels() {
const labels = GmailApp.getUserLabels();
let html = '';
labels.forEach(l => {
html += `<br>Name: ${l.getName()}`
});
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html),'Label Names');
}
I modified your code to test the label names:
function gMailReminder() {
const rlbl = "Qs/Services/Google";
const label = GmailApp.getUserLabelByName(rlbl);
const threads = label.getThreads();
let html ='';
if (threads.length > 0) {
threads.forEach(t => {
html += `<br>${t.getFirstMessageSubject()}`;
});
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html),'Subjects');
}
}
And this now works
Came up with the solution myself.
As you can see, my label has Chinese characters and GmailApp.Search has no problem with this.
However, LABEL_DONE requires you to put in I guess the unicode (or whatever it's called) of the Chinese characters. Again, I don't know anything about Javascript and so a lot of my code is just copying and pasting code from somewhere else and modifying them to the best of my extent. I had to do a bit of debugging to find out what my Chinese label looks like in the Logger. The result provided by the Logger is the unicode that you see for LABEL_DONE. If you also use non-English characters for your labels, you'll need to test it yourself and find what the corresponding unicode string is.
Anyway, the code below will use GmailApp.Search to search for specific labels (you can mix and match search conditions in there and can test this easily in Gmail search itself). It will then add the emails for those specific labels into Google Calendar, then apply a "Bill Reminder - Processed" label. The next time the Google App Script trigger runs, it will skip these emails, because of the Processed label.
// https://stackoverflow.com/questions/12377640/printing-to-the-console-in-google-apps-script
function testLog(){
Logger.log("Logger.log");
console.log("console.log")
}
function BillReminder_Rates() {
if (!GmailApp) return; // Skip script execution if GMail is currently not available (yes this happens from time to time and triggers spam emails!)
var reminderLabel = "電費,-水費,-垃圾袋,-網路-bill-reminder---processed", //Substitute your label here
calendarName = "Reminders", ////Substitute your Calendar name here
reminderDuration27Days = 648; //duration in hours;
var threads = GmailApp.search('(label:電費,-水費,-垃圾袋,-網路-council-rates---me) AND (-label:電費,-水費,-垃圾袋,-網路-bill-reminder---processed)');
if (threads.length > 0) {
//get calendar by name
var cals = CalendarApp.getCalendarsByName(calendarName);
var now = new Date().getTime();
for (i in threads) {
//cals[0].createEvent(reminderLabel + ' - '+threads[i].getFirstMessageSubject(),
cals[0].createEvent(threads[i].getFirstMessageSubject(),
new Date(now+(60000*60*reminderDuration27Days)),
new Date(now+(60000*60*reminderDuration27Days)), {description: threads[i].getPermalink()});
}
LABEL_DONE = "\u96fb\u8cbb, \u6c34\u8cbb, \u5783\u573e\u888b, \u7db2\u8def/Bill Reminder - Processed";
var label_done = GmailApp.getUserLabelByName(LABEL_DONE);
for (var t in threads) {
var threadblah = threads[t];
// Grab the task data
var taskTitle = threadblah.getFirstMessageSubject();
// Insert the task
//addTask_(taskTitle, TASKLIST);
// Set to 'done' by exchanging labels
//threadblah.removeLabel(label_pending);
threadblah.addLabel(label_done);
}
}
}
function BillReminder_Power_Gas() {
if (!GmailApp) return; // Skip script execution if GMail is currently not available (yes this happens from time to time and triggers spam emails!)
var reminderLabel = "電費,-水費,-垃圾袋,-網路-bill-reminder---processed", //Substitute your label here
calendarName = "Reminders", ////Substitute your Calendar name here
reminderDuration12Days = 288; //duration in hours;
var threads = GmailApp.search('(label:電費,-水費,-垃圾袋,-網路-power -OR label:電費,-水費,-垃圾袋,-網路-gas) AND (-label:電費,-水費,-垃圾袋,-網路-bill-reminder---processed)');
if (threads.length > 0) {
//get calendar by name
var cals = CalendarApp.getCalendarsByName(calendarName);
var now = new Date().getTime();
for (i in threads) {
//cals[0].createEvent(reminderLabel + ' - '+threads[i].getFirstMessageSubject(),
cals[0].createEvent(threads[i].getFirstMessageSubject(),
new Date(now+(60000*60*reminderDuration12Days)),
new Date(now+(60000*60*reminderDuration12Days)), {description: threads[i].getPermalink()});
}
LABEL_DONE = "\u96fb\u8cbb, \u6c34\u8cbb, \u5783\u573e\u888b, \u7db2\u8def/Bill Reminder - Processed";
var label_done = GmailApp.getUserLabelByName(LABEL_DONE);
for (var t in threads) {
var threadblah = threads[t];
// Grab the task data
var taskTitle = threadblah.getFirstMessageSubject();
// Insert the task
//addTask_(taskTitle, TASKLIST);
// Set to 'done' by exchanging labels
//threadblah.removeLabel(label_pending);
threadblah.addLabel(label_done);
}
}
}
function BillReminder_Water() {
if (!GmailApp) return; // Skip script execution if GMail is currently not available (yes this happens from time to time and triggers spam emails!)
var reminderLabel = "電費,-水費,-垃圾袋,-網路-bill-reminder---processed", //Substitute your label here
calendarName = "Reminders", ////Substitute your Calendar name here
reminderDuration15Days = 360; //duration in hours;
var threads = GmailApp.search('(label:電費,-水費,-垃圾袋,-網路-watercare---me) AND (-label:電費,-水費,-垃圾袋,-網路-bill-reminder---processed)');
if (threads.length > 0) {
//get calendar by name
var cals = CalendarApp.getCalendarsByName(calendarName);
var now = new Date().getTime();
for (i in threads) {
//cals[0].createEvent(reminderLabel + ' - '+threads[i].getFirstMessageSubject(),
cals[0].createEvent(threads[i].getFirstMessageSubject(),
new Date(now+(60000*60*reminderDuration15Days)),
new Date(now+(60000*60*reminderDuration15Days)), {description: threads[i].getPermalink()});
}
LABEL_DONE = "\u96fb\u8cbb, \u6c34\u8cbb, \u5783\u573e\u888b, \u7db2\u8def/Bill Reminder - Processed";
var label_done = GmailApp.getUserLabelByName(LABEL_DONE);
for (var t in threads) {
var threadblah = threads[t];
// Grab the task data
var taskTitle = threadblah.getFirstMessageSubject();
// Insert the task
//addTask_(taskTitle, TASKLIST);
// Set to 'done' by exchanging labels
//threadblah.removeLabel(label_pending);
threadblah.addLabel(label_done);
}
}
}
function delete_events()
{
//take care: Date function starts at 0 for the month (January=0)
//{search: 'cycle'+"*"+'Checkpoint'} hier zijn de search terms
var fromDate = new Date(2021,6,1,0,0,0); //This is July 1, 2021
var toDate = new Date(2021,11,1,0,0,0); //This is September 1, 2021 at 00h00'00"
var calendarName = 'Reminders';
var toRemove = 'title_of_the_events';
var calendar = CalendarApp.getCalendarsByName(calendarName)[0];
var events = calendar.getEvents(fromDate, toDate,{search: toRemove});
for(var i=0; i<events.length;i++)
{
var ev = events[i];
if(ev.getTitle()==toRemove) //check if the title matches
{
Logger.log('Item '+ev.getTitle()+' found on '+ev.getStartTime()); // show event name and date in log
ev.deleteEvent(); //uncomment this line to actually do the delete !
}
}
}
function delete_events2()
{
var fromDate = new Date(2021,8,1,0,0,0);
var toDate = new Date(2021,11,27,0,0,0);
var calendarName = 'Reminders';
// delete from Jan 1 to end of Jan 4, 2013 (for month 0 = Jan, 1 = Feb...)
var calendar = CalendarApp.getCalendarsByName(calendarName)[0];
var events = calendar.getEvents(fromDate, toDate);
for(var i=0; i<events.length;i++){
var ev = events[i];
Logger.log(ev.getTitle()); // show event name in log
ev.deleteEvent();
}
}
So I managed to combine Google form, google calendar, as well as google sheets. When people submit the form (with a start date and end date), it will automatically appear in the google sheets as well as google calendar.
I modified the script to find conflict (to prevent double-booking), however, I just realized that even when the same person is trying to edit starting and ending date (via edit response), it will still show CONFLICT.
For example, someone books from date April 15th to April 17th, and he decided to change to April 16th to April 18th, because he previously booked 15-17, his new submission is having conflict with his own previous submission.
How can I add a function that will detect the same email to edit and submit data? (within empty day slot. Thanks in advance!
This is the function to create an object from sheet data
//Calendars to Output appointments
var cal = CalendarApp.getCalendarById('ID');
// Create an object from user submission
function Submission(){
var row = lastRow;
this.timestamp = sheet.getRange(row, 1).getValue();
this.email = sheet.getRange(row, 2).getValue();
this.name = sheet.getRange(row, 3).getValue();
this.date = sheet.getRange(row, 4).getValue();
this.dateout = sheet.getRange(row, 5).getValue();
this.room = sheet.getRange(row, 6).getValue();
this.day = sheet.getRange(row,8).setFormula("=(R[0]C[-3]-R[0]C[-4])+1").getValue();
var fillDownRange = sheet.getRange(2, 8, lastRow-1);
sheet.getRange(row,8).copyTo(fillDownRange);
// Info not from spreadsheet
this.status;
this.dateString = (this.date.getMonth() + 1) + '/' + this.date.getDate() + '/' + this.date.getYear();
this.dateOutString = (this.dateout.getMonth() + 1) + '/' + this.dateout.getDate() + '/' + this.dateout.getYear();
this.calendar = eval('cal' + String(this.room));
return this;
}
This is the function to detect conflict
function getEndTime(request){
request.endTime = new Date(request.dateout.getTime() + 24 * 60 * 60 * 1000);
}
// Check for appointment conflicts
function getConflicts(request){
var conflicts = request.calendar.getEvents(request.date, request.endTime);
if (conflicts.length < 1) {
request.status = "Approve";
} else {
request.status = "Conflict";
}
}
This is the function to edit calendar
//update calendar (add)
function updateCalendar(request){
var event = request.calendar.createEvent(
"Booked",
request.date,
request.endTime
)
}
Instead of always retrieving the last row, you should retrieve the actual row of the latest submission
Mind that if people update their Google Form response, the submission row in the spreadsheet will not change - only the content.
You can retrieve the latest submitted / modified form response row with the event object event.range (provided your function is bound to a Google Sheets form submit trigger)
You can compare the modified row to the last row in the sheet
If the form response row is equal to the last row - a new response has been submitted
Sample:
function bindMeOnFormSubmitTrigger(event){
var sheet = SpreadsheetApp.getActive().getActiveSheet();
var lastRow = sheet.getLastRow();
var row = event.range.getRow();
if (row == lastRow){
// insert a new event as done before
...
}
else {
// the submitted response was an edit of a previously submitted one
// identify the already existing event (e.g. if you specified the respondent email address in the event description) and modify the event data to the newly submitted values
var newFormResponseValues = event.values;
this.timestamp = newFormResponseValues[0];
...
}
}
If the form response row is differernt (smaller) then the last row in the sheet - it means that an existing form response has been edited (and consequently the event data might need to be modified in the calendar).
If you want to acccess the email of the respondent, you have to previously activate this option in the Google Forms UI settings:
I found a much better solution by using e.range. The better way to use this method is to create two separate sheets inside same spreadsheet. The main sheet will first retrieve response from the form, and then automatically create a copy to the second sheet. And if there is edited submission, it will go through the second sheet to find the edited row, then modify the row (as well as the calendar event). Credit to Tedinoz
Feel free to add comment/copy, cheers
function updateCalendarTwo(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var responsename = "Main sheet"
var copyname = "Copy Sheet";
var responsesheet = ss.getSheetByName(responsename);
var copysheet = ss.getSheetByName(copyname);
var calendar = CalendarApp.getCalendarById('Your Calendar ID');
// columns on copysheet
var checkInCol = 4;
var checkOutCol = 5;
var roomNumCol = 6;
var appCol = 11
var eventIDCol = 12;
var revCol = 14;
var response = e.range;
var rRow = response.getRow()
var rLC = responsesheet.getLastColumn();
var cLC = copysheet.getLastColumn();
var rLR = responsesheet.getLastRow();
var cLR = copysheet.getLastRow();
if (rLR > cLR){
var resprange = responsesheet.getRange(rLR,1,1,rLC);
var respdata = resprange.getValues();
copysheet.appendRow(respdata[0]);
var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
var startDate = copysheet.getRange(rRow,checkInCol).getValue();
var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
if (conflicts.length < 1) {
var event = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
var eventID = event.getId().split('#')[0];
copysheet.getRange(rRow,appCol).setValue("approve");
copysheet.getRange(rRow,eventIDCol).setValue(eventID);
} else {
copysheet.getRange(rRow,appCol).setValue("conflict");
}
} else {
var resprange = responsesheet.getRange(rRow,1,1,9);
var respdata = resprange.getValues();
var copyrespRange = copysheet.getRange(rRow,1,1,9);
copyrespRange.setValues(respdata);
var respAppRange = copysheet.getRange(rRow,appCol);
var respApp = respAppRange.getValue();
if (respApp == 'conflict') {
var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
var startDate = copysheet.getRange(rRow,checkInCol).getValue();
var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
if (conflicts.length < 1) {
var editedEvent = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
var editedEventID = editedEvent.getId().split('#')[0];;
copysheet.getRange(rRow,appCol).setValue("edited");
copysheet.getRange(rRow,eventIDCol).setValue(editedEventID);
} else {
copysheet.getRange(rRow,appCol).setValue("conflict");
};
} else {
var eventEditId = copysheet.getRange(rRow,eventIDCol).getDisplayValue();
var editedEvent = calendar.getEventSeriesById(eventEditId);
editedEvent.deleteEventSeries();
var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
var startDate = copysheet.getRange(rRow,checkInCol).getValue();
var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
if (conflicts.length < 1) {
var editedEvent = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
var editedEventID = editedEvent.getId().split('#')[0];;
copysheet.getRange(rRow,appCol).setValue("edited");
copysheet.getRange(rRow,eventIDCol).setValue(editedEventID);
} else {
copysheet.getRange(rRow,appCol).setValue("conflict");
};
};
var revRange = copysheet.getRange(rRow,revCol);
var revOldValue = revRange.getValue();
if (revOldValue == null || revOldValue == ""){
revOldValue = 0;
}
var revNewValue = revOldValue+1;
revRange.setValue(revNewValue);
}
}
first time poster so I apologise if if im not following the correct etiquette.
So I have code that, in essence, creates 70 forecast docs in specified folders. This works fine.
I have used the same code, repointed the all the folder references, but now times out.
Code is as follows, any suggestions on how I can make this run faster?
function createFiles(weeknr, masterIDs, masterfolders, mastercodes, archivefolders) {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var date = new Date()
var textdate = Utilities.formatDate(date, "Europe/London", "dd/MM/YYYY HH:mm")
var fileslog = ss.getSheetByName("FileList")
var year = date.getYear()
var mainfolder = DriveApp.getFolderById("1HdG3eJZrIMF-CRpx17Reqdyd7JM5v5ZW")
var weekfolder = mainfolder.createFolder('Week ' + weeknr + ' ' + year)
var tempArray = []
for (var i = 0; i < masterIDs.length; i++) {
// for (var i=0;i<masterIDs.length;i++){
var folderID = masterfolders[i]
var masterID = masterIDs[i]
var code = mastercodes[i]
var archivefolder = archivefolders[i]
var newfile = DriveApp.getFileById(masterID).makeCopy(code + ' DCS WK' + weeknr,
DriveApp.getFolderById(folderID))
var newID = newfile.getId()
tempArray.push([newfile.getName(), newID, newfile.getUrl(),
newfile.getDateCreated(), "Created", folderID, archivefolder, weeknr])
SpreadsheetApp.openById(newID).getSheetByName("Main").getRange(3, 3).setValue(weeknr)
weekfolder.addFile(newfile)
//NEW UPDATE 14/02 to set the owner to network finance
newfile.setOwner('network.finance#dpdgroup.co.uk')
//End of UPDATE
fileslog.getRange(fileslog.getLastRow() + 1, 1, 1, 8).setValues([tempArray[i]])
}
// Logger.log(tempArray )
// if(tempArray.length>0){
// fileslog.getRange(fileslog.getLastRow()+1, 1, tempArray.length,
8).setValues(tempArray)
// }
}
function archiveFiles(weeknr, fileIDs, masterfolders, mastercodes, archivefolders) {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var date = new Date()
var textdate = Utilities.formatDate(date, "Europe/London", "dd/MM/YYYY HH:mm")
var fileslog = ss.getSheetByName("FileList")
//var year = date.getYear()
//var mainfolder = DriveApp.getFolderById("1HdG3eJZrIMF-CRpx17Reqdyd7JM5v5ZW")
//var weekfolder = mainfolder.createFolder('Week '+weeknr+' '+year)
var tempArray = []
for (var i = 0; i < fileIDs.length; i++) {
// for (var i=0;i<masterIDs.length;i++){
var folderID = masterfolders[i]
var fileID = DriveApp.getFileById(fileIDs[i])
var code = mastercodes[i]
var archivefolder = archivefolders[i]
DriveApp.getFolderById(archivefolder).addFile(fileID)
DriveApp.getFolderById(folderID).removeFile(fileID)
//var newfile = DriveApp.getFileById(masterID).makeCopy(code + ' DCS WK'+weeknr,
DriveApp.getFolderById(folderID))
//var newID = newfile.getId()
tempArray.push([fileID.getName(), fileIDs[i], fileID.getUrl(),
fileID.getLastUpdated(), "Archived", masterfolders[i], archivefolders[i], weeknr])
//weekfolder.addFile(newfile)
}
Logger.log(tempArray)
if (tempArray.length > 0) {
fileslog.getRange(fileslog.getLastRow() + 1, 1, tempArray.length, 8).setValues(tempArray)
}
}
Posted the answer for documentation purpose.
As discussed in the comments the problem was the file size, splitting the copied files in batches did the trick:
So turns out, the file was simply too large to create 70 files in the allotted time. So I split it down to batches of 30 and its worked perfectly. thanks for your help
Here is my case.
A user fills a form for event booking, the submitted form is stored in a google spreadsheet which I have synced to a google calender so that it automatically sends the data to it.
Everything is working fine apart from the fact that event times could clash.
When customers book an event centre for let's say on 13/3/2015 T 10:00AM, if another user enters the same date and time, the entry should not be accepted.
To summarise it, I want to avoid a clash of events booking. Thank you all.
here is my script.
var calendarId = "mycalenderid";
//below are the column ids of that represents the values used in the spreadsheet (these are non zero indexed)
var startDtId = 9;
var endDtId = 10;
var titleId = 6;
var descId = 11;
var formTimeStampId = 1;
function getLatestAndSubmitToCalendar() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var lr = rows.getLastRow();
var startDt = sheet.getRange(lr,startDtId,1,1).getValue();
//set to first hour and minute of the day.
//startDt.setHours(0);
//startDt.setMinutes(00);
var endDt = sheet.getRange(lr,endDtId,1,1).getValue();
//set endDt to last hour and minute of the day
//endDt.setHours(23);
//endDt.setMinutes(59);
var subOn = "Submitted on:"+sheet.getRange(lr,formTimeStampId,1,1).getValue();
var desc = "Added by :"+sheet.getRange(lr,descId,1,1).getValue()+"\n"+subOn;
var title = sheet.getRange(lr,titleId,1,1).getValue()+"DIA";
createEvent(calendarId,title,startDt,endDt,desc);
}
function createEvent(calendarId,title,startDt,endDt,desc) {
var cal = CalendarApp.getCalendarById(calendarId);
var start = new Date(startDt);
var end = new Date(endDt);
var loc = 'Script Center';
var event = cal.createEvent(title, start, end, {
description : desc,
location : loc
});
};
Here's a pseudocode of what you're trying to do:
function findEvent(desiredDateTime)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var range = sheet.getDataRange();
var data = range.getValues();
var lRow = range.getLastRow();
var flag = true;
var count = 0;
while (flag == true || count < lRow)
{
if (desiredDateTime >= data[count][startDtId] && desiredDateTime <= data[count][endDtId])
{
flag = false;
}
else
{
count++;
}
}
if (flag == true)
{
//Call function to add event
}else{
//Tell user desired date-time is not available.
//If you're asking for user's email address,
//simplest approach would be to send an email.
}
}
You might have to modify other bits and pieces of your code as well to accommodate this but it shouldn't be too hard. Hope this provides you with a certain direction to follow through.
I have been trying to figure out Google Apps Script validation in Google Sites and have yet to make the validation part work correctly.
I need to validate 2 things:
That there is at least 5 characters in the "location" textbox (up to 100)
That a date has been selected from the dropdown
If both conditions are not met, then it should make visible 2 things:
warnException
warnExceptionMes
That's it.
The rest of my logic is working great. I am just starting out.
The full logic is listed below. I have replaced our domain info with xxxxxxxxx.
So far, it either never shows the messages and does nothing or just getting one of the items right allows it to move forward. They should both meet the requirements or the warnExceptions should be thrown. This would also be the same if a user loaded the page and did not fill either/or area out and just pushed the button.
How can I validate the Location textbox and the dateBox?
var templateIDToCopy = 'xxxxxxxxxx';
var folderIDtoCopyTo = 'xxxxxxxxxx';
var councilMembers = ['Unknown','Name 1','Name 2'];
function doGet(e) {
var text= new Array();
var app = UiApp.createApplication();
var hpanel = app.createGrid(4, 6).setId('pannel');
var hpanelException = app.createGrid(2,3).setId('hpanelException');
var location = app.createTextBox().setName('location').setId("location").setWidth('200');
var minuteTaker = app.createListBox().setName('minuteTaker').setId("minuteTaker").setWidth('200')
for (var i = 0 ; i < councilMembers.length; i++) {
minuteTaker.addItem(councilMembers.valueOf()[i]);
}
var dateBox = app.createDateBox().setId('dateBox').setName('dateBox').setFireEventsForInvalid(false);
var hour = app.createListBox(false).setId('hour').setName('hour')
// var hour = app.createListBox(false).setId('hour').setName('hour')
for(h=1;h<13;++h){hour.addItem(h)}
var min = app.createListBox(false).setId('minute').setName('minute')
.addItem('00').addItem('15').addItem('30').addItem('45');
var amPm = app.createListBox(false).setId('am').setName('amPm')
.addItem('AM').addItem('PM');
var dateTimeLabel = app.createLabel('',false).setId('dateTimeLabel');
var submit = app.createButton('Create Minutes').setId('submit').setPixelSize(196, 25);
var nextSteps = app.createAnchor('Please click here to see the minutes archive.', 'https://drive.google.com/xxxxxxxxxx/folderview?xxxxxxxxxx').setId('nextSteps').setVisible(false);
// Setup error message
var warnException =app.createImage('https://sites.google.com/xxxxxxxxxx/minutes/create-new-minutes/Opps.png').setId('warnException').setVisible(false);
var warnExceptionMes = app.createLabel('The date and Location are required. Please try again.').setStyleAttribute('font-weight', 'normal').setStyleAttribute('font-size','14px').setVisible(false);
// handlers
var handler1 = app.createClientHandler()
.validateLength(location, 0, 50).validateMatches(dateBox, '2', 'g')
.forTargets(warnException).setVisible(true)
.forTargets(warnExceptionMes).setVisible(true);
var handler2 = app.createServerHandler('handlerFunction')
.validateLength(location, 1, 100).validateNotMatches(dateBox, '2', 'g')
.addCallbackElement(location).addCallbackElement(dateBox).addCallbackElement(hpanel);
submit.addClickHandler(handler1).addClickHandler(handler2);
hpanel.setWidget(0,0,app.createLabel('Select Date'))
.setWidget(0,1,app.createLabel('Hour'))
.setWidget(0,2,app.createLabel('Minutes'))
.setWidget(0,3,app.createLabel('AM/PM'))
.setWidget(0,4,app.createLabel('Location'))
.setWidget(0,5,app.createLabel('Minute Taker'))
hpanel.setWidget(1,0,dateBox)
.setWidget(1,1,hour)
.setWidget(1,2,min)
.setWidget(1,3,amPm)
.setWidget(1,4,location)
.setWidget(1,5,minuteTaker)
hpanel.setWidget(2,5,submit)
app.add(hpanel);//.add(warnException).add(warnExceptionMes);
hpanelException.setWidget(1,1,warnException).setStyleAttribute("text-align", "right")
.setWidget(1,2,warnExceptionMes)
// .setWidget(1,2,nextSteps)
app.add(hpanelException);
return app;
}
function handlerFunction(e) {
var app = UiApp.getActiveApplication();
app.getElementById('submit').setText('Building, please wait...').setEnabled(false);
var location = e.parameter.location;
var determineName = e.parameter.minuteTaker;
var date = e.parameter.dateBox;
var timeZone = date.toString().substr(25,6)+":00";
var dateMilli = date.getTime();
var hour = parseInt(e.parameter.hour);
var amPm = e.parameter.amPm;
if (amPm == 'PM' && hour != 12) hour = hour + 12;
if (hour == 12 && amPm == 'AM') hour = 0;
var hourMilli = hour * 3600000;
var minMilli = parseInt(e.parameter.minute) * 60000;
var milliTotal = dateMilli + hourMilli + minMilli;
// create custom format
var newDate = Utilities.formatDate(new Date(milliTotal), timeZone, 'MM/dd/yy hh:mm aaa');
app.getElementById('dateTimeLabel').setText(newDate);
// make a copy of the minutes template to use
var duplicateID = DriveApp.getFileById(templateIDToCopy)
.makeCopy('Simply Minutes v1.0 - Stage 1: Building new minutes...')
.getId();
// get the id of the annual folder where minutes will be stored
var getFolderID = DriveApp.getFolderById(folderIDtoCopyTo);
// copy new minutes sheet to the annual folder where minutes are stored
var moveIT = DriveApp.getFileById(duplicateID).makeCopy('Simply Minutes v1.0 - Stage 2: Building new minutes...', getFolderID).getId();
// get the new minutes doc that was created
var template = DocumentApp.openById(moveIT);
var templateHeader = template.getHeader();
var templateBody = template.getActiveSection();
// fill in the values
templateHeader.replaceText("<date>", newDate);
templateBody.replaceText("<date>", newDate);
templateHeader.replaceText("<location>", location);
templateBody.replaceText("<location>", 'N/A');
var email = Session.getEffectiveUser().getEmail();
var eUser = Session.getEffectiveUser().getUsername();
var createdBy = '';
if(ContactsApp.getContact(email)){
var fullName = ContactsApp.getContact(email).getFullName();
createdBy = fullName;
}
else {
createdBy = 'N/A';
};
var determineName = e.parameter.minuteTaker;
templateHeader.replaceText("<minutetaker>", determineName);
templateHeader.replaceText("<createdby>", createdBy)
templateBody.replaceText("<minutetaker>", determineName);
templateBody.replaceText("<createdby>", createdBy);
template.setName(newDate + ' TAC Minutes Recorded By ' + determineName);
// close out the doc
template.saveAndClose();
// remove the copy that was left in the root directory
// DriveApp.getFileById(duplicateID).isTrashed();
DriveApp.getFileById(duplicateID).setTrashed(true);
app = UiApp.getActiveApplication();
app.getElementById('submit').setText('Completed!').setEnabled(false);
app.getElementById('nextSteps').setVisible(true);
return app;
}
Try like this (see below) I changed a bit the validations and separated in 2 handlers + added a "clear" handler to be able to click-to-delete the warnings... test code here
You have also to add something to clear the warnings in the server handler and, why not combine the image and text warning in a single widget ? (easier to clean)
code below :
function doGet(e) {
var text= new Array();
var app = UiApp.createApplication();
var hpanel = app.createGrid(4, 6).setId('pannel');
var clearHandler = app.createClientHandler().forEventSource().setVisible(false)
var hpanelException = app.createGrid(2,3).setId('hpanelException');
var location = app.createTextBox().setName('location').setId("location").setWidth('200');
var minuteTaker = app.createListBox().setName('minuteTaker').setId("minuteTaker").setWidth('200')
for (var i = 0 ; i < councilMembers.length; i++) {
minuteTaker.addItem(councilMembers.valueOf()[i]);
}
var dateBox = app.createDateBox().setId('dateBox').setName('dateBox').setFireEventsForInvalid(false);
var hour = app.createListBox(false).setId('hour').setName('hour')
// var hour = app.createListBox(false).setId('hour').setName('hour')
for(h=1;h<13;++h){hour.addItem(h)}
var min = app.createListBox(false).setId('minute').setName('minute')
.addItem('00').addItem('15').addItem('30').addItem('45');
var amPm = app.createListBox(false).setId('am').setName('amPm')
.addItem('AM').addItem('PM');
var dateTimeLabel = app.createLabel('',false).setId('dateTimeLabel');
var submit = app.createButton('Create Minutes').setId('submit').setPixelSize(196, 25);
var nextSteps = app.createAnchor('Please click here to see the minutes archive.', 'https://drive.google.com/xxxxxxxxxx/folderview?xxxxxxxxxx').setId('nextSteps').setVisible(false);
// Setup error message
var warnException =app.createImage('https://sites.google.com/xxxxxxxxxx/minutes/create-new-minutes/Opps.png').setId('warnException').setVisible(false).addClickHandler(clearHandler);
var warnExceptionMes = app.createLabel('The date and Location are required. Please try again.').setStyleAttribute('font-weight', 'normal').setStyleAttribute('font-size','14px').setVisible(false).addClickHandler(clearHandler);
// handlers
var handler0 = app.createClientHandler()
.validateLength(location, 0, 5)
.forTargets(warnException).setVisible(true)
.forTargets(warnExceptionMes).setVisible(true);
var handler1 = app.createClientHandler()
.validateNotMatches(dateBox, '2', 'g')
.forTargets(warnException).setVisible(true)
.forTargets(warnExceptionMes).setVisible(true);
var handler2 = app.createServerHandler('handlerFunction')
.validateLength(location, 6, 100).validateMatches(dateBox, '2', 'g')
.addCallbackElement(location).addCallbackElement(dateBox).addCallbackElement(hpanel);
submit.addClickHandler(handler0).addClickHandler(handler1).addClickHandler(handler2);
hpanel.setWidget(0,0,app.createLabel('Select Date'))
.setWidget(0,1,app.createLabel('Hour'))
.setWidget(0,2,app.createLabel('Minutes'))
.setWidget(0,3,app.createLabel('AM/PM'))
.setWidget(0,4,app.createLabel('Location'))
.setWidget(0,5,app.createLabel('Minute Taker'))
hpanel.setWidget(1,0,dateBox)
.setWidget(1,1,hour)
.setWidget(1,2,min)
.setWidget(1,3,amPm)
.setWidget(1,4,location)
.setWidget(1,5,minuteTaker)
hpanel.setWidget(2,5,submit)
app.add(hpanel);//.add(warnException).add(warnExceptionMes);
hpanelException.setWidget(1,1,warnException).setStyleAttribute("text-align", "right")
.setWidget(1,2,warnExceptionMes)
// .setWidget(1,2,nextSteps)
app.add(hpanelException);
return app;
}
EDIT second version following your comment.
I simulated a server function that takes some time so you see all the steps. + I combined the warning as suggested. (demo code updated with version 2)
var templateIDToCopy = 'xxxxxxxxxx';
var folderIDtoCopyTo = 'xxxxxxxxxx';
var councilMembers = ['Unknown','Name 1','Name 2'];
function doGet(e) {
var text= new Array();
var app = UiApp.createApplication();
var hpanel = app.createGrid(4, 6).setId('pannel');
var hpanelException = app.createGrid(2,3).setId('hpanelException');
var location = app.createTextBox().setName('location').setId("location").setWidth('200');
var minuteTaker = app.createListBox().setName('minuteTaker').setId("minuteTaker").setWidth('200')
for (var i = 0 ; i < councilMembers.length; i++) {
minuteTaker.addItem(councilMembers.valueOf()[i]);
}
var dateBox = app.createDateBox().setId('dateBox').setName('dateBox').setFireEventsForInvalid(false);
var hour = app.createListBox(false).setId('hour').setName('hour')
// var hour = app.createListBox(false).setId('hour').setName('hour')
for(h=1;h<13;++h){hour.addItem(h)}
var min = app.createListBox(false).setId('minute').setName('minute')
.addItem('00').addItem('15').addItem('30').addItem('45');
var amPm = app.createListBox(false).setId('am').setName('amPm')
.addItem('AM').addItem('PM');
var dateTimeLabel = app.createLabel('',false).setId('dateTimeLabel');
var submit = app.createButton('Create Minutes').setId('submit').setPixelSize(195, 65);
var nextSteps = app.createAnchor('Please click here to see the minutes archive.', 'https://drive.google.com/xxxxxxxxxx/folderview?xxxxxxxxxx').setId('nextSteps').setVisible(false);
var clearHandler = app.createClientHandler();
// Setup error message
var warnException =app.createImage('https://dl.dropboxusercontent.com/u/211279/clock_e0.gif').addClickHandler(clearHandler);
var warnExceptionMes = app.createLabel('The date and Location are required. Please try again.').setStyleAttribute('font-weight', 'normal').setStyleAttribute('font-size','14px').addClickHandler(clearHandler);
var warnPanel = app.createHorizontalPanel().add(warnException).add(warnExceptionMes).setId('warning').setVisible(false);
clearHandler.forTargets(warnPanel).setVisible(false);
// handlers
var handler0 = app.createClientHandler()
.validateLength(location, 0, 5)
.forTargets(warnPanel).setVisible(true)
var handler1 = app.createClientHandler()
.validateNotMatches(dateBox, '2', 'g')
.forTargets(warnPanel).setVisible(true)
var handler2 = app.createClientHandler()
.validateLength(location, 6, 100).validateMatches(dateBox, '2', 'g')
.forEventSource().setText('Server Handler is running...').setEnabled(false)
.forTargets(warnPanel).setVisible(false);
var handlerS = app.createServerHandler('handlerFunction')
.validateLength(location, 6, 100).validateMatches(dateBox, '2', 'g')
.addCallbackElement(location).addCallbackElement(dateBox).addCallbackElement(hpanel);
submit.addClickHandler(handler0).addClickHandler(handler1).addClickHandler(handler2).addClickHandler(handlerS);
hpanel.setWidget(0,0,app.createLabel('Select Date'))
.setWidget(0,1,app.createLabel('Hour'))
.setWidget(0,2,app.createLabel('Minutes'))
.setWidget(0,3,app.createLabel('AM/PM'))
.setWidget(0,4,app.createLabel('Location'))
.setWidget(0,5,app.createLabel('Minute Taker'))
hpanel.setWidget(1,0,dateBox)
.setWidget(1,1,hour)
.setWidget(1,2,min)
.setWidget(1,3,amPm)
.setWidget(1,4,location)
.setWidget(1,5,minuteTaker)
hpanel.setWidget(2,5,submit)
app.add(hpanel);
hpanelException.setWidget(1,1,warnPanel).setStyleAttribute("text-align", "right")
app.add(hpanelException);
return app;
}
function handlerFunction(e) {
var app = UiApp.getActiveApplication();
Utilities.sleep(1000);
app.getElementById('submit').setText('SERVER HANDLER is DONE');
// app.getElementById('warning').setVisible(false);// not necassary anymore, see clientHandler2
return app;
}