Create calendar event from Gmail? - javascript

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();
}
}

Related

Javascript can't write to a file

I want the code to write datalogs to JStest.csv file after 5 clicks done by the user are completed, and I have created a CSV file call JStest in the Downloads folder. But when I run my code, it says it can't find this file. Please help !
This is the error message I got:
Error: ENOENT: no such file or directory, open '/Downloads/JStest.csv'
Here's my code:
const app = require('electron').remote.app;
const fileDir = app.getPath('desktop');
const path = require("path");
var fs = require('fs');
// this will hold all the data we need
var dataLog = "";
// this will count how many clicks have occured
var clicks = 0;
var maxTrials = 5;
var iconArray = document.getElementById("iconDrawer");
// reference our start button
var startButton = document.getElementById("startBtn");
// display how many tasks a user has completed
var counterDisplay = document.getElementById("counter");
// display the target icon to click (find the element with this HTML tag)
var indicator = document.getElementById("indicator");
// element that holds all your icons
var parent = document.getElementById("iconDrawer");
// array of all icons (hint: use the parent var to reference its children icons)
var icons = parent.children;
var i0 = icons[0];
var i1 = icons[1];
var i2 = icons[2];
var i3 = icons[3];
var i4 = icons[4];
/////////////////////////////////////////////////////////////////////////////////////
// TODO: Set the filepath, so you know where the .csv file is going!
/////////////////////////////////////////////////////////////////////////////////////
function save()
{
// change the filepath in the writeFile() function
fs.writeFile( path.resolve(fileDir, "/Users/lidaochang/Desktop/Downloads/JStest.csv"), dataLog, (err) => {
if (err) alert(err);
alert("all tasks are done");
});
}
function randomIcon()
{
// Generate a random number in the appropriate range
var iconIndex = Math.random()*4;
iconIndex = Math.round(iconIndex);
return iconIndex;
}
var timedClick = function()
{
// disables the start button, so user can't press it twice
//startButton.onclick = function(){};
// call randomIcon function to get random index and the matching icon
var targetIndex = randomIcon();
var targetIcon = icons[targetIndex];
indicator.src = targetIcon.src;
// start timing right here
var startTime = performance.now();
// this is where we are going to start watching for clicks on icons
// this loop will add an onclick function to each icon
for(var i=0; i < icons.length; i++)
{
icons[i].onclick = function()
{
// everything in here will occur when an icon is pressed
// stop timing and record how long it took
// calculate time elapsed
// record whole milliseconds
var endTime = performance.now();
var timeTaken = endTime - startTime;
timeTaken = Math.round(timeTaken);
alert("that took " + timeTaken);
// only 1 icon can be clicked at a time, so disable all the onclicks now!
// loop through all the icons and disable the function like we did with the start button
for(var j=0; j < icons.length; j++) {
if (j != targetIcon)
icons[j].onclick = function(){};
}
// record the time and positions of the target icon and the icon the user actually pressed
// this is to be stored in a new line in the 'dataLog' variable
// append to the 'dataLog' variable
var iconClicked = this.id[1]; // INCLUDE THIS
var data = "";
data.concat(i, " ", targetIndex, " ", iconClicked, " ", timeTaken);
// add to the end of the 'dataLog' variable as explained above
dataLog += data;
// increment clicks completed
clicks += 1;
// update what the counterDisplay says!
// modify the innerHTML property of counterDisplay
// it shows the user how many clicks have currently been completed
counterDisplay.innerHTML = clicks + " tasks out of 5 completied";
// if maxTrials is reached, then data collection is over, so call save and reset 'clicks' and 'dataLog'
if (clicks == maxTrials) {
save();
clicks = 0;
dataLog = "";
}
to starting the trial
startButton.onclick = timedClick;
}
}
}
window.onload = function()
{
startButton.onclick = timedClick;
}

Google form edit response submission, automatically modify data in sheets, as well as calendar event

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);
}
}

sending data from a google spreadsheet program to google calender

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.

How to remove localstorage once per day?

I have the following code which caches in local storage a big set of terms from sharepoint.
As the source data might change once or twice per day, I would like to include that if its a new day, then it should get fresh data.
App is used by 400 people so caching improved it a lot, from 15 seconds load to less than 1 second, but I need to be sure at least once new data is loaded.
Question is:
How can I invalidate this key each day?
function GetAllTermsRecursive(){
if(typeof(Storage) !== "undefined")
{
//Check if data has been cached.
if(typeof(localStorage.Terms) !== "undefined")
{
var start = new Date().getTime();
var terms=localStorage.Terms;
var end = new Date().getTime();
var time = end - start;
alert('Execution time with cache: ' + time);
}
else
{
var start = new Date().getTime();
//Current Context
var context = SP.ClientContext.get_current();
//Current Taxonomy Session
var taxSession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
//Term Stores
var termStores = taxSession.get_termStores();
//Name of the Term Store from which to get the Terms.
var termStore = termStores.getByName("Taxonomy_kl5tZjInn7STsFTzIE7n3Q==");
//GUID of Term Set from which to get the Terms.
var termSet = termStore.getTermSet("31da4bc1-6429-499a-9d5e-be5e18b13c87");
var terms = termSet.getAllTerms();
context.load(terms);
context.executeQueryAsync(function(){
var termEnumerator = terms.getEnumerator();
var termList = "Terms: \n";
while(termEnumerator.moveNext()){
var currentTerm = termEnumerator.get_current();
var currentTermPath = currentTerm.get_pathOfTerm().split(';');
var children = tree.children;
termList += currentTermPath.get_name();
termList += currentTerm.get_name() + "\n";
}
localStorage.Terms = termList;
var end = new Date().getTime();
var time = end - start;
alert('Execution time: ' + time);
},function(sender,args){
console.log(args.get_message());
});
}
}
else {
alert("Cache not sopported in old browsers, please upgrade");
}
}

Google Apps Script validation issue in Google UiApp

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;
}

Categories

Resources