Jump to content

Push or Export events to non-Caspio calendars (Apple, Google, Outlook, etc) from submission form


Recommended Posts

This thread has parts and answers in other threads, but none of them solve doing this in a Submission form so I thought a new thread that unified this info would be helpful to anyone who needs this kind of solution. 

@Meekeee has posted a really cool solution for pushing calendar events via download from Caspio to Google, Apple, MS calendars, which is extremely beneficial: https://jekuer.github.io/add-to-calendar-button/ and https://github.com/jekuer/add-to-calendar-button.

There's a few issues in using this solution with Caspio, though, that I haven't been able to figure out--mostly when it's deployed in a submission form. The issue is you need to get the user's input from the submission form to populate the values for the exported event's name, description, BeginDateTime, EndDateTime, and Location. On a Details page it's fairly straightforward, using [@field:BeginDate], etc. But the [@field:..] method isn't available on a Submission form, only Authentication fields can be used that way. 

I've tried using variables to get the field values (and testing with a 'dead' button and alert box confirmed that the values were captured correctly). The problem is I don't know how to use those variables in the event export code that goes into the datapage (there are other files, css, js, etc. that are referenced from elsewhere). 

This is the code that gets the values and coverts the date/time field to ISO formatting for the export: 

function getevent(){
var v_name = document.getElementById('InsertRecordTasksTimeline_Title').value;
var v_note = document.getElementById('InsertRecordTasksTimeline_Notes').value;
var v_location = document.getElementsByName('InsertRecordTasksTimeline_Location')[0].value;

 Stamp = new Date(document.getElementById('InsertRecordTasksTimeline_CalDate').value);
 Hours = Stamp.getHours()
 Mins = Stamp.getMinutes();
v_BeginDate=('' + Stamp.getFullYear() +"-"+ (Stamp.getMonth()+1) + "-" + Stamp.getDate() +"T"+  Hours + ":" + Mins);

Stamp2 = new Date(document.getElementById('InsertRecordTasksTimeline_CalEnd').value);
 Hours = Stamp2.getHours()
 Mins = Stamp2.getMinutes();
v_EndDate=('' + Stamp2.getFullYear() +"-"+ (Stamp2.getMonth()+1) + "-" + Stamp2.getDate() +"T"+  Hours + ":" + Mins);

}

This is @Meekeee's code (there's variations, I'm using one that uses a normal button to call it) that creates the button with the event download options: 

<button id="default-button" name="submitevent">Save/Close</button>

<script type="application/javascript">

  const config = {

    "name": "tile of event",
    "description": "notes for the event",
    "startDate" : "2022-05-21T10:15",
    "endDate": "2022-05-21T12:30",
    "location":"location of event",
    options: ["Google", "Apple","Microsoft365"],
    timeZone: "currentBrowser",
    trigger: "click",
    iCalFileName: "Reminder-Event",
  }
  const button = document.querySelector('#default-button')
  button.addEventListener('click', ()=> atcb_action(config, button)
)
</script>

The issue is getting the variable values into the " " part of the code. For example, "name": "tile of event", will work with "name": "[@authfield:Company]", but I can't figure out how to use the variables. It would be something like this (but it doesn't work, though): 

<button id="default-button" name="submitevent">Save/Close</button>

<script type="application/javascript">

  const config = {
    
    var v_name = document.getElementById('InsertRecordTasksTimeline_Title').value;
    var v_note = document.getElementById('InsertRecordTasksTimeline_Notes').value;
    var v_location = document.getElementsByName('InsertRecordTasksTimeline_Location')[0].value;

 Stamp = new Date(document.getElementById('InsertRecordTasksTimeline_CalDate').value);
    Hours = Stamp.getHours()
    Mins = Stamp.getMinutes();
    v_BeginDate=('' + Stamp.getFullYear() +"-"+ (Stamp.getMonth()+1) + "-" + Stamp.getDate() +"T"+  Hours + ":" + Mins);

Stamp2 = new Date(document.getElementById('InsertRecordTasksTimeline_CalEnd').value);
    Hours = Stamp2.getHours()
    Mins = Stamp2.getMinutes();
    v_EndDate=('' + Stamp2.getFullYear() +"-"+ (Stamp2.getMonth()+1) + "-" + Stamp2.getDate() +"T"+  Hours + ":" + Mins);

    "name": "v_name",
    "description": "n_note",
    "startDate" : "v_BeginDate",
    "endDate": "v_EndDate",
    "location":"v_location",
    options: ["Google", "Apple","Microsoft365"],
    timeZone: "currentBrowser",
    trigger: "click",
    iCalFileName: "Reminder-Event",
  }
  const button = document.querySelector('#default-button')
  button.addEventListener('click', ()=> atcb_action(config, button)
)
</script>

Does anyone know how to integrate the variables with the event export code? This would be a really great solution if it can be worked out. 

 

Link to comment
Share on other sites

Hi @DesiLogi -  for the Start Date and End Date, you should have a separate variable for getting the actual Date and Time.

As per their Important reminder:
https://github.com/jekuer/add-to-calendar-button#:~:text=Important information

The Date should be formatted as YYYY-MM-DD and the time as HH:MM. In this case, you may need to convert the date and time.

I have this sample script for converting the date/time and how you will call the variable in the script.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/add-to-calendar-button@1.8/assets/css/atcb.min.css">
<script src="https://cdn.jsdelivr.net/npm/add-to-calendar-button@1.8" defer></script>

<button type="button" id="default-button">Download Calendar</button>

<script type="application/javascript">

const sday = document.getElementById('InsertRecordDATEFIELD').value;
const eday = document.getElementById('InsertRecordDATEFIELD').value;

const c_sday = new Date(sday).toLocaleDateString('fr-CA');
const c_eday = new Date(eday).toLocaleDateString('fr-CA');

const time_start = new Date(sday);
const time_end = new Date(eday);

const t_start = time_start.toLocaleTimeString('en-GB', {
  hour: '2-digit',
  minute: '2-digit',
});

const t_end = time_end.toLocaleTimeString('en-GB', {
  hour: '2-digit',
  minute: '2-digit',
});

  const config = {
    name: "[@field:FIELDNAME]",
    description: "[@field:FIELDNAME]",
    startDate: c_sday,
    endDate: c_eday,
    startTime: t_start,
    endTime: t_end,
    location:"[@field:FIELDNAME]",
    options: ["iCal","Outlook.com","Yahoo","Apple","Microsoft365","MicrosoftTeams"],
    timeZone: "currentBrowser",
    trigger: "click",
    iCalFileName: "Event Test",
  }
  const button = document.querySelector('#default-button')
  button.addEventListener('click', () => atcb_action(config, button))


</script>

I hope this helps!

Link to comment
Share on other sites

Hi @Meekeee,

Thanks for updating the thread--hopefully I can get this worked out. For some reason I can't get this code to work at all, even when I pasted it into the submission form verbatim (with the Date field changed to my own). A couple things I think that are relevant:  

-- this line doesn't work in a submission form, as a submission form doesn't allow pulling @field:fieldname values (only @authfield:fieldname on a submission form). 

name: "[@field:FIELDNAME]",

--I tried using the date/time formatting lines you posted but I couldn't get them to work. However, the code I have for getting the Begin/End Date/Time variables does work in the Details/Update form I used this on, putting a date time into ISO format: YYYY-MM-DDTHH:MM which has gone into the calendars without an issue (from Details/Update forms).  So I don't think the date/time formatting is the issue. 

It seems like the issue is the "const" lines that get the values and then put them in the config code. That's not working here--not sure why. 

 

Link to comment
Share on other sites

Hi @DesiLogi - as you are using a Submission Form - calling Fieldnames won't work as this is a new data.

May I know when do you trigger the script in the form? Is it after submitting data or before? Or it is just a button?

As this is used for downloads, all the variables must have data before downloading the file.

Link to comment
Share on other sites

Hi @Meekeee,

I'm using your custom button to trigger the script (I couldn't get any other methods to work for some reason--probably a Caspio issue). This is the button: <button id="default-button" name="submitevent3">Save/Close</button>. When clicked it calls the script  below, which opens the export buttons overlay. But it ALSO will submit the form after about 3 seconds, whether the user clicked on anything or not:

<script type="application/javascript">

  const config = {

    "name": "tile of event",
    "description": "notes for the event",
    "startDate" : "2022-05-21T10:15",
    "endDate": "2022-05-21T12:30",
    "location":"location of event",
    options: ["Google", "Apple","Microsoft365"],
    timeZone: "currentBrowser",
    trigger: "click",
    iCalFileName: "Reminder-Event",
  }
  const button = document.querySelector('#default-button')
  button.addEventListener('click', ()=> atcb_action(config, button)
)
</script>

I can get and format the variables for the new info without a problem (I tested the below code with a separate button and alert boxes and the data is correct, and date/time formatted correctly). 

var v_name = document.getElementById('InsertRecordTasksTimeline_Title').value;
var v_note = document.getElementById('InsertRecordTasksTimeline_Notes').value;
var v_location = document.getElementsByName('InsertRecordTasksTimeline_Location')[0].value;

 Stamp = new Date(document.getElementById('InsertRecordTasksTimeline_CalDate').value);
 Hours = Stamp.getHours()
 Mins = Stamp.getMinutes();
v_BeginDate=('' + Stamp.getFullYear() +"-"+ (Stamp.getMonth()+1) + "-" + Stamp.getDate() +"T"+  Hours + ":" + Mins);

Stamp2 = new Date(document.getElementById('InsertRecordTasksTimeline_CalEnd').value);
 Hours = Stamp2.getHours()
 Mins = Stamp2.getMinutes();
v_EndDate=('' + Stamp2.getFullYear() +"-"+ (Stamp2.getMonth()+1) + "-" + Stamp2.getDate() +"T"+  Hours + ":" + Mins);

The variables v_name, v_note, v_location, v_BeginDate, and v_EndDate need to be put in "name", "description", "startDate", "endDate", "location",

So the issues for getting this to work in a Caspio submission datapage is: 

a) holding off on submitting the form till the user is done with the calendar event export options

b) taking the new data variables (v_name, v_note, v_location, v_BeginDate, and v_EndDate) and putting them in the config code so they are used in the export

c) opening the export buttons as normal so the user can choose an export option OR x out of the overlay

d) after the user chooses and export option (or x's out), then submit the Caspio submission form as normal.

Link to comment
Share on other sites

Hi @Meekeee,

I've almost got the export worked out with a submission form (I got some help from Caspio) and will post that when it's finalized. One question in the meantime--is there a way to reposition the buttons when using the custom button method? The custom button is on the bottom of the submission form and so when the user clicks it the Event Export button options show up below it, mostly out of range. It would be great if the buttons could go vertical above the custom button instead of below. 

Calendar+Export+Position.png

The code I'm using (will update this thread when the last details are done) is 

const config = {

name: v_name,
description: v_note,
startDate: c_sday,
endDate: c_eday,
startTime: stime,
endTime: etime,
location:"World Wide Web",
options: ["Google", "Apple","Microsoft365"],
timeZone: "currentBrowser",
trigger: "click",
iCalFileName: "Reminder-Event",
}

const button = document.querySelector('#default-button')
button.addEventListener('click', ()=> atcb_action(config, button)
)

Is there a line I can put in there to make the export option buttons go vertical up instead of down? 

Many thanks!

Link to comment
Share on other sites

Ok, with some gracious help from Caspio (thanks Mykee!) I was able to get the variables into the config file.  I also figured out how to place the export buttons where I want them on the form (via a hidden button) and to use 2 button options for submission (Save/Close and Save/Add Another).  So this solution is almost complete. The last thing it needs it to actually submit the form but ONLY after the user has clicked an Event Export option or X'd out of it. I still don't know how to do that. 

Once that's worked out I'll post the whole solution in a concise manner so it's easy to replicate. 

Link to comment
Share on other sites

Thanks @Meekeee--that really helps with the button list placement. The last thing that needs to happen for this to work on a submission page is for the form to submit after the user has either clicked on an export button or X'd out of the overlay. I've been trying some code at the end of the script but can't get it to work--can you take a look at it to see what's off? 

function submitCBForm() {
document.getElementById("caspioform").submit();
}

const button = document.querySelector('.atcb_list,.atcb_bgoverlay');
button.addEventListener('click', submitCBForm)

 

Link to comment
Share on other sites

Hi @DesiLogi,

At the end of your script you can add the ff:

 

function submitCBForm() {
document.getElementById("caspioform").submit();
}

var intervalId = window.setInterval(function(){
const submitForm = document.querySelector('.atcb_list');

if (submitForm === null) {

} else {

clearInterval(intervalId);
submitForm.addEventListener('click', submitCBForm)
}

}, 500);

 

The reason behind setting a timer is because the .actb_list element technically does not exist yet until a user clicks on the Save/Close button, so the timer would be responsible in checking if the .actb_list element finally exists, and it checks it every 0.5 seconds. Now, once the Save/Close has finally been clicked, the .actb_list finally exists, then it proceeds with the submit function you have.

Hope this helps!

 

Link to comment
Share on other sites

Hi @futurist,

Many thanks for this solution--I'd tried a bunch of things and just couldn't get it to work right. The delay of 500 is key, I didn't realize the sequence of creating the .actb_list element. 

One last thing, though--if the user doesn't choose one of the Export buttons and instead clicks the 'X' to close the overlay, it doesn't submit but still needs to. This is because the user may not want to export the event and just submit it as normal in Caspio. So when the Export overlay opens they'll click 'X' to close the overlay and expect the Caspio form to submit anyway. 

I tried adding the other classes involved (.actb_click and .actb_bgoverlay) as OR choices but that didn't work. In fact, I substituted each for .actb_list to test and neither of those classes did anything so I don't think using them is the solution. 

It seems to me that the way to run the Submit code is to check when .actb_list is closed (if it had been opened to begin with). Does that make sense? How would that  be included in the submit solution? 

Calendar+Export+Submit+choices.png

Link to comment
Share on other sites

@Meekeee or @futurist-- any chance there's a solution to this? I've been trying to figure it out but so far no good. this is the last piece of this puzzle, and if it's not possible to Submit the Caspio form if the user X's out of the export options then the UX is just not going to work. I really hope this is doable after coming this far-- 

Link to comment
Share on other sites

  • 2 weeks later...

Hi @DesiLogi - you may try the same code by @futurist. Here's the modified code:

<script>

function submitCBForm() {
document.getElementById("caspioform").submit();
}

var intervalId = window.setInterval(function(){
const submitForm = document.querySelector('.atcb_bgoverlay');
const submitForm1 = document.querySelector('.atcb_list');

if (submitForm === null || submitForm1 === null) {
  
} 

else {

clearInterval(intervalId);
submitForm.addEventListener('blur', submitCBForm);
submitForm1.addEventListener('click', submitCBForm);
}

}, 500);

</script>

The blur event fires when an element has lost focus which is the bgoverlay. You may check these links for reference:
https://developer.mozilla.org/en-US/docs/Web/API/Window/blur_event
https://developer.mozilla.org/en-US/docs/Web/API/Window/focus_event

Link to comment
Share on other sites

  • 3 weeks later...

Hi @Meekeee,

Sorry it took so long to get back to this thread but I was finally able to revisit and implement this last piece of the puzzle--perfection! This is exactly what the whole process needed, now it's a really seamless UI for the Caspio user. Many thanks (to @futurist as well) for offering this solution and following up with the necessary tweaks to integrate it into Caspio.  Really an excellent calendar solution I imagine a lot of people on Caspio will use. 

Link to comment
Share on other sites

  • 1 month later...

Hi @Meekeee,

The calendar export is working great--one final question (I hope!): how would this be used in a Calendar datapage itself? I have it in a Submission and a Single Record update form but when I use the method in a Calendar datapage (from the main calendar view) it doesn't seem to catch the correct date or times upon export. I think it has something to do with the export not anchoring to the correct record (it seems to go to the last record in the month view record set).  The export needs to be date-specific. 

cal+export+example.png

What I have is the Export button in an HTML block in the Calendar results section: 

<button type="button"  id="default-button" name="submitevent3"><i class="fa fa-calendar-plus-o" aria-hidden="true"></i> Export Event</button>

Then I have the config code in the footer: 

<script>

//***EXPORT calendar events

  const config = {

   "name":"[@field:TasksTimeline_Title]",
    "description":"[@field:TasksTimeline_Notes]",
     "location":"[@field:TasksTimeline_Task_Cat]",
    "startDate":"[@field:TasksTimeline_CalDateISO]",
    "endDate":"[@field:TasksTimeline_CalEndISO]",
    options: [
"Apple",
    "Google",
    "iCal|ics file",
    "Microsoft365",
    "MicrosoftTeams",
    "Outlook.com",
],
    timeZone: "currentBrowser",
    trigger: "click",
    iCalFileName: "Reminder-Event",
  }
  const button = document.querySelector('#default-button')
  button.addEventListener('click', ()=> atcb_action(config, button))

</script>

The 'TasksTimeline_CalDateISO' fields contain both the date and time, formatted in ISO for export--works perfectly on the Submission and Update forms so I don't think it's that. It seems like the problem is that the config code is not getting the date-specific record that the export button is being clicked from. It defaults to the last record in the recordset. 

Any idea how to modify this code to pull the specific record the export button is clicked from? If this'll work it'll make a really nice overall calendar UX. 

Thanks again for all the help with this!

Edited by DesiLogi
put in image of issue
Link to comment
Share on other sites

  • 1 month later...

Hi - Good news! Caspio has a new Tech tip called Downloading Appointments to Calendars Using ICS Files. To know more on how to implement it in the DataPage, you can check it here: https://howto.caspio.com/tech-tips-and-articles/advanced-customizations/download-appointments-to-calendars-using-ics-files/

@DesiLogi - you may find the solution in this article under Inserting a button to download ICS files from your Calendar Results Page. Hope it helps!

Link to comment
Share on other sites

  • 3 months later...

Hi, sharing in here a script that I found that allows you to check if an element exists (followed by your code if that element in fact exists) instead of using a timer:

 

function waitForElm(selector) {
    return new Promise(resolve => {
        if (document.querySelector(selector)) {
            return resolve(document.querySelector(selector));
        }

        const observer = new MutationObserver(mutations => {
            if (document.querySelector(selector)) {
                resolve(document.querySelector(selector));
                observer.disconnect();
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    });
}

 

 

 

 

To use it:

waitForElm('.some-class').then((elm) => {
    console.log('Element is ready');
    console.log(elm.textContent);
});
Link to comment
Share on other sites

  • 3 months later...
On 6/3/2022 at 11:27 AM, Meekeee said:

Hi @DesiLogi -  for the Start Date and End Date, you should have a separate variable for getting the actual Date and Time.

As per their Important reminder:
https://github.com/jekuer/add-to-calendar-button#:~:text=Important information

The Date should be formatted as YYYY-MM-DD and the time as HH:MM. In this case, you may need to convert the date and time.

I have this sample script for converting the date/time and how you will call the variable in the script.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/add-to-calendar-button@1.8/assets/css/atcb.min.css">
<script src="https://cdn.jsdelivr.net/npm/add-to-calendar-button@1.8" defer></script>

<button type="button" id="default-button">Download Calendar</button>

<script type="application/javascript">

const sday = document.getElementById('InsertRecordDATEFIELD').value;
const eday = document.getElementById('InsertRecordDATEFIELD').value;

const c_sday = new Date(sday).toLocaleDateString('fr-CA');
const c_eday = new Date(eday).toLocaleDateString('fr-CA');

const time_start = new Date(sday);
const time_end = new Date(eday);

const t_start = time_start.toLocaleTimeString('en-GB', {
  hour: '2-digit',
  minute: '2-digit',
});

const t_end = time_end.toLocaleTimeString('en-GB', {
  hour: '2-digit',
  minute: '2-digit',
});

  const config = {
    name: "[@field:FIELDNAME]",
    description: "[@field:FIELDNAME]",
    startDate: c_sday,
    endDate: c_eday,
    startTime: t_start,
    endTime: t_end,
    location:"[@field:FIELDNAME]",
    options: ["iCal","Outlook.com","Yahoo","Apple","Microsoft365","MicrosoftTeams"],
    timeZone: "currentBrowser",
    trigger: "click",
    iCalFileName: "Event Test",
  }
  const button = document.querySelector('#default-button')
  button.addEventListener('click', () => atcb_action(config, button))


</script>

I hope this helps!

Hi! This is old, but hopefully you see it. I found your post, as I am trying to accomplish an 'add to calendar' button, and using your script the button is not clickable. Nothing happens - can you point me in the right direction to troubleshoot this perhaps? Thanks!

Link to comment
Share on other sites

Hi @Lauren,

I ended up using a single fields with ISO format that includes markup for delineating the time. There are various reasons for me not using the standard 2 fields (one for date, one for time) because of a lot of other stuff in the existing app, but combining into ISO format works well. 

In terms of where to start diagnosing, it's hard to say. The code is fairly complex and I had a LOT of problems getting it to finally work with my particular datapage. I ended up having to break it down function by function, to test where the problems were. First, just make sure the fields have the right data in them and then test that Meekee's solution is pulling them correctly (I use 'alerts' placed in successive stages of code to make sure lines are working). After I make sure of the basics (often using a command button that runs the script, for testing) then I'll massage it into the datapage's UI. 

I realize this is pretty vague--hope it helps find a place to start, though, and good luck! 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...