Event Calendar: How to Collect and Approve Event Submissions via Form Builder

Friends, many of you have asked for a way to let website visitors submit events to your Event Calendar widget.

While this is not supported out of the box yet, our awesome devs came with a great solution:

  • visitors submit events via Form Builder

  • you approve them in Google Sheets

  • approved events are pushed to your Google Calendar

  • Event Calendar widget pulls them in automatically


If you want to try this on your site, follow the steps below. We’ll walk you through the full setup :wink:

What are the steps? :memo:


Create Form Builder to collect events

The 1st step is to create a Form Builder widget to let users submit events to your calendar, The Form Builder widget must include the following fields:

  1. Short text — Event name (required)
  2. Long text — Event description
  3. File — Event image
  4. Dropdown — Event type
  • Scheduled (default)
  • All day
  1. Date — Start date (required)
  2. Time — Start time

    Condition: show only if the event type is set to “Scheduled”

  3. Date — End date (required)
  4. Time — End time

Condition: show only if the event type is set to “Scheduled”

  1. Dropdown — Event repeat
  • Does Not Repeat (default)


Once all the fields are created, open the Integrations section and enable the Google Sheets integration:


Create a calendar in Google Calendar
  1. Click on the “+” button in the Other Calendars section in the Google Calendar and create a new calendar (name can be anything)



  1. The new calendar will appear in My Calendars section. Click on the 3 dots next to this calendar and choose Settings and Sharing

  1. Scroll down to the Integrate calendar section and save your Calendar ID. You’ll need it later:


Set up Google Spreadsheet

Fill in the form and submit a test event. The connected spreadsheet should then contain these columns:

  • Event name — Event name
  • Event description — Event description
  • Event image — Event image
  • Event type — Event type
  • Start date — Start date
  • Start time — Start time
  • End date — End date
  • End time — End time
  • Event repeat — Event repetition
  • Date — Additional field for selecting a date (if needed)

After that add an additional column called Approved.


Add Apps Script integrations
  1. Open Extensions → Apps Script.

  1. Create a script file named constants and add:
const CALENDAR_ID = "your_calendar_id@group.calendar.google.com";

Do not forget to replace "your_calendar_id@group.calendar.google.com" with the ID of your Calendar from the step 2.

  1. Create a file named testCalendarAccess and add:
function testCalendarAccess() {
  const cal = CalendarApp.getCalendarById(CALENDAR_ID);
  Logger.log("Calendar object: " + cal);
  if (cal) Logger.log("Calendar name: " + cal.getName());
}


  1. While in testCalendarAccess, click Run. The execution log should open.
  • If you see an error, try again.
  • After successful authorization, the execution log should show script messages like:
    • Calendar object: Calendar
    • Calendar name: calendar name

  1. Create the following script files:
  • utils
function getHeader(sheet) {
  return sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
}
function findColumn(header, name) {
  return header.indexOf(name) + 1;
}
function getHoursMinutes(value) {
  if (!value) return null;
  
  if (value instanceof Date) {
    return {
      h: value.getHours(),
      m: value.getMinutes()
    };
  }
  if (typeof value === 'number') {
    const totalMinutes = Math.round(value * 24 * 60);
    return {
      h: Math.floor(totalMinutes / 60) % 24,
      m: totalMinutes % 60
    };
  }
  if (typeof value === 'string') {
    const match = value.match(/(\d+):(\d+)/);
    if (match) {
      return { h: parseInt(match[1]), m: parseInt(match[2]) };
    }
  }
  return null;
}
  • getEventValues
function getEventValues(e) {
  const sheet = e.source.getActiveSheet();
  const header = getHeader(sheet);
  const range = e.range;
  const row = range.getRow();
  const eventNameCol = findColumn(header, "Event name");
  const eventDescriptionCol = findColumn(header, "Event description");
  const eventImageCol = findColumn(header, "Event image");
  const startDateCol = findColumn(header, "Start date");
  const endDateCol = findColumn(header, "End date");
  const startTimeCol = findColumn(header, "Start time");
  const endTimeCol = findColumn(header, "End time");
  const eventName = sheet.getRange(row, eventNameCol).getValue();
  const eventDescription = sheet.getRange(row, eventDescriptionCol).getValue();
  const eventImage = sheet.getRange(row, eventImageCol).getValue();
  const startDate = sheet.getRange(row, startDateCol).getValue();
  const endDate = sheet.getRange(row, endDateCol).getValue();
  const startTime = startTimeCol ? sheet.getRange(row, startTimeCol).getValue() : null;
  const endTime = endTimeCol ? sheet.getRange(row, endTimeCol).getValue() : null;
  return { eventName, eventDescription, eventImage, startDate, endDate, startTime, endTime };
}
  • onEditApproved
function onEditApproved(e) {
  const sheet = e.source.getActiveSheet();
  const header = getHeader(sheet);
  const range = e.range;
  const approvedCol = findColumn(header, "Approved");
  if (!approvedCol || range.getColumn() !== approvedCol) {
    return;
  }
  const editedValue = String(e.value || "").toLowerCase();
  if (editedValue === "approved") {
    const { eventName } = getEventValues(e);
    createCalendarEvent(e);
    Logger.log(eventName + " approved and added to Calendar");
  }
}
  • createCalendarEvent
function createCalendarEvent(e) {
  const {
    eventName,
    eventDescription,
    eventImage,
    startDate,
    endDate,
    startTime,
    endTime
  } = getEventValues(e);
  
  if (!eventName || !startDate || !endDate) {
    return;
  }
  
  let start, end;
  
  if (typeof startDate === 'string') {
    const [day, month, year] = startDate.split('.');
    start = new Date(year, month - 1, day);
  } else {
    start = new Date(startDate);
  }
  
  if (typeof endDate === 'string') {
    const [day, month, year] = endDate.split('.');
    end = new Date(year, month - 1, day);
  } else {
    end = new Date(endDate);
  }
  
  if (startTime && endTime) {
    const s = getHoursMinutes(startTime);
    const en = getHoursMinutes(endTime);
    
    if (s && en) {
      start.setHours(s.h, s.m, 0, 0);
      end.setHours(en.h, en.m, 0, 0);
    }
  }
  
  const calendar = CalendarApp.getCalendarById(CALENDAR_ID);
  if (!calendar) {
    Logger.log("Calendar not found: " + CALENDAR_ID);
    return;
  }
  
  const descriptionParts = [];
  if (eventImage) {
    descriptionParts.push(
      `<a href="${eventImage}" target="_blank" aria-hidden="true"></a>`
    );
  }
  if (eventDescription) {
    descriptionParts.push(eventDescription);
  }
  const fullDescription = descriptionParts.join("\n\n");
  
  try {
    calendar.createEvent(eventName, start, end, {
      description: fullDescription
    });
    Logger.log(eventName + " approved and added to Calendar");
  } catch (error) {
    Logger.log("Error creating event: " + error.message);
  }
}
  1. Go to Triggers and click Create a new trigger:
  • Choose function: onEditApproved
  • Deployment: Head
  • Event source: From spreadsheet
  • Event type: On edit



Create the Event Calendar widget

Create the Event Calendar widget and connect to your Google Calendar


Publish submitted events to your calendar

You’re all set.

Open your Form Builder widget (created in the 1st step) and the connected Google Sheet. Each new submission will land there as a new row. To publish an event to your calendar, type Approved in the Approved column for that row:

image


This video demonstrates the feature in action:


Have you tested this solution yet? Please let us know if it worked for you - we’re all ears :wink:

3 Likes

@Max would it be possible to have the form in the list of available forms - so it is one less step to do?

2 Likes

Hi @Phil :waving_hand:

Yep, we can share a link to the Form containing all the needed fields, but in this case you can just use it as a reference, which I guess won’t save you much time.

The good news is that we can duplicate the same form to your account and then you’ll be able to customize it the way you need. Would you like to go ahead?

1 Like

I have done it now @Max I am on the next stage, but can’t get past that part. The apps script side of it, it keeps failing. Have a Google Calendar created and Google Sheet, but the other part, I am struggling with. I’ll keep trying tonight.

2 Likes

If the scripts still fail, please send me a link with the access to your spreadsheet in a personal message.

For this, click on the Share button in your spreadsheet and copy a link from the dropdown:

We’ll be happy to look into this :wink:

1 Like

I like this idea, but with Google Calendar as the intermediary are you limited to only the event parameters that can be passed through Google Calendar? We need to leverage the full set of parameters available in the Elfsight Event Calendar widget itself.

1 Like

Hi there, @Demo_Acct :waving_hand:

Yep, with this workaround you can pass only the following elements due to the Google Calendar restrictions:

  1. Event name *(required)
  2. Event description
  3. Event image
  4. Type of event: Scheduled ot All day
  5. Start date
  6. Start time
  7. End date
  8. End time

If you’d like to pass more elements like event types, tags, etc, a more native solution is required. This idea is already on the Wishlist and you can upvote it here :slightly_smiling_face:- Allow users to add events to the calendar

1 Like

Relatively simple steps to follow. We have implemented this on our site, however we have two issues before we go live with the widget. First, it is setting the time as GMT so when imported to Google Calendar it is 5 hours back as we are NYT. Second, it does not seem to be repeating. What is the syntax for repeating events and how can we fix the timezone import? Let me know if you would like links to any portion.

1 Like

Hi there, @Connect_Downtown_Joh :waving_hand:

Thank you for questions!

I’ll discuss with the devs if it’s possible to submit recurring events. I’ll update you once I have their response :slightly_smiling_face:

As for the time zone, please make sure the correct time zone is selected in your Google Sheets file and your Google Calendar.

Google Calendar:

Click on the Gear icon, select Settings and set the correct Time Zone



Google Spreadsheet:

Click on the File, select Settings and set the needed time zone:


Please check it out and let me know if it helped :slightly_smiling_face:

Changing the timezone on the spreadsheet corrected it going forward! When the Elfsight Form generated the Google Sheets it set it to GMT. Thanks for the quick reply!

1 Like

Great, you’re very welcome!

Regarding recurring events, unfortunately, with this workaround, it’s not possible to submit them. I’m really sorry about that!

If you have any questions left, I’ll be happy to help :slightly_smiling_face: