Button to create Google Calendar event based on the current tiddler

Here’s how to create a view toolbar button that, upon click, will tag the tiddler “In calendar” and open an external URL that allows to easily add an event to Google Calendar. A similar workflow should be possible for any other calendars or web apps that allow to input data by a special URL.

The button:

Calendar entry opened in new tab after clicking the button:

Tiddler tagged with “In calendar” afterwards:

The button in JSON: $ _wilk_buttons_add-to-calendar.json (1.2 KB)

The button in TID format:

caption: {{$:/core/images/new-journal-button|1em|G}} add to calendar
description: Add to calendar
tags: $:/tags/ViewToolbar
title: $:/wilk/buttons/add-to-calendar

\whitespace trim

\procedure .addToCalendarActions()
<$action-sendmessage $message="tm-add-tag" $param="In calendar"/>
\end

<$fieldmangler>
<$button tooltip={{$:/wilk/buttons/add-to-calendar!!description}} class=<<tv-config-toolbar-class>> actions=<<.addToCalendarActions>> >
	<a href={{{
		[[https://calendar.google.com/calendar/r/eventedit]]
		=[[?text=]]
		=[<currentTiddler>encodeuricomponent[]]
		=[[&details=]]
		=[{$:/info/url/full}append[#]encodeuricomponent[]]
		=[<currentTiddler>encodeuricomponent[]encodeuricomponent[]]
		=[[&dates=]append<now YYYY0MM0DD>append[/]append<now YYYY0MM0DD>]
		+[join[]]
	}}} target="_blank">
		<$list filter="[<tv-config-toolbar-icons>match[yes]]">{{$:/core/images/new-journal-button|1em|G}}</$list>
		<$list filter="[<tv-config-toolbar-text>match[yes]]"><span class=tc-btn-text>Add to calendar</span></$list>
	</a>
</$button>
</$fieldmangler>

See details about constructing the Google Calendar event URL.

Some explanations about the URL:

  • text parameter (event name) is set to current tiddler’s name. encodeuricomponent[] is required to properly encode a name containing spaces or other special characters in the URL.
  • details parameter (event details/description) is set to a link to the current tiddler. The tricky part is, the name of the tiddler has to go through encodeuricomponent[] twice, because it will be decoded once by Calendar, but we need special characters like space to remain encoded.
  • dates parameter is set to current date in format 20231213/20231213. This makes the event an all day event. Going by the book, it should be 20231213/20231214 (day range where last day is exclusive), but it works this way as well for single day events.

Possible improvements and preferential adjustments:

  • More details could be added to the event, e.g. exact time, location, wikified contents of the tiddler as details.
  • A different (custom) icon could be used, the current one {{$:/core/images/new-journal-button|1em|G}} is just modified one from “new journal here” button to display “G” instead of the current date: image
  • What action is performed on the tiddler is adjustable through the .addToCalendarActions procedure. I chose tagging with “In calendar” for a simple demonstration. For example in a task management wiki, one could choose to mark the task as complete (since it is scheduled in calendar and one does not need to keep track of it in the wiki anymore).
  • I can’t get the link to open in Google Calendar app on Android, it brings me to the URL in the browser. Maybe it’s possible, but depends on browser/OS settings.
  • There is weird spacing and underline of the button if hidden in the overflow menu: image

This button is surely most useful in some kind of task management wiki without calendar functionality (I created it for my GSD wiki), but I can imagine it could be occasionally handy in a general note taking wiki.

Thanks to @TW_Tones for figuring out how to combine button actions with external URL in this thread.

10 Likes

Detailed and comprehensive. Kudos @vilc !

1 Like

Looking for making it compatible with FOOS “NextCloud” : Calendar integration — Nextcloud latest Developer Manual latest documentation

1 Like

I added a revised version of your button to TW-Tweaks (a personal tiddlywiki tweaks).
I also changed the details of event to include the tiddler text!
It has a new icon!

Go to GitHub,
From docs, download index.htmlI will fix the demo page

I made a couple of changes on my version too. The event description will contain a “See tiddler” stable link (works even if tiddler is renamed, as long as the created field remains the same, as discussed here: Is there a way to create a link [permalink] to a tiddler that doesn’t break if the tiddler name changes?) to the tiddler, as well as wikified html of the tiddler’s text.

The enormous filter expression that constructs the event URL is difficult to edit now, as it contains a lot of uri-encoded strings, this is an area for further improvement, if the button should be easy to customize.

Just for information, as it has been pointed out in another thread, the situation where a <button> element contains an interactive element like <a> is against HTML specs. However, this is probably the only way to achieve the desired effect using wikitext without resorting to JS.

My button in TID format:

caption: {{$:/core/images/new-journal-button|1em|G}} add to calendar
description: Add to calendar
tags: $:/tags/ViewToolbar $:/wilk/tweaks/AddToCalendar
title: $:/wilk/buttons/add-to-calendar

\whitespace trim

\procedure .addToCalendarActions()
<$action-sendmessage $message="tm-add-tag" $param="In calendar"/>
\end

<$wikify name="wikifiedText" text={{!!text}} output="html">
<$fieldmangler>
<$button tooltip={{$:/wilk/buttons/add-to-calendar!!description}} class=<<tv-config-toolbar-class>> actions=<<.addToCalendarActions>> >
	<a href={{{
		[[https://calendar.google.com/calendar/r/eventedit]]
		=[[?text=]]
			=[<currentTiddler>encodeuricomponent[]]
		=[[&details=]]
			=[[%3Ca%20href%3D%22]]
			=[{$:/info/url/full}encodeuricomponent[]]
			=[[%23%3A%5Bcreated%5B]]
			=[{!!created}]
			=[[%5D%5D%2520%5Bfield:title%5B]]
			=[<currentTiddler>encodeuricomponent[]encodeuricomponent[]]
			=[[%5D%5D]]
			=[[%22%3ESee%20tiddler%3C%2Fa%3E]]
			=[[%0A%3Chr%3E]]
			=[<wikifiedText>encodeuricomponent[]]
		=[[&dates=]append<now YYYY0MM0DD>append[/]append<now YYYY0MM0DD>]
		+[join[]]
	}}} target="_blank">
		<$list filter="[<tv-config-toolbar-icons>match[yes]]">{{$:/core/images/new-journal-button|1em|G}}</$list>
		<$list filter="[<tv-config-toolbar-text>match[yes]]"><span class=tc-btn-text>Add to calendar</span></$list>
	</a>
</$button>
</$fieldmangler>
</$wikify>

Some screenshots to demonstrate:

image

After clicking the button you get:

1 Like

See this post: How to Control the Generated href via Wikilink - #16 by pmario

@vilc below is a version of your code that avoids the need to nest a link inside a button, and avoids the performance hit of wikify until the button is clicked. I have skipped the implementation of the permalink but that should not be difficult to add by modifying the get.details function.

\whitespace trim

\procedure linkTemplate() $(get.calendar.base.URI)$?text=$(get.encoded.title)$&details=$(get.details)$&dates=$(get.dates)$
\function get.calendar.base.URI() https://calendar.google.com/calendar/u/0/r/eventedit
\function get.encoded.title() [<currentTiddler>encodeuricomponent[]]
\function get.details() [<wikifiedText>encodeuricomponent[]]
\function get.dates() [<now YYYY0MM0DD>addsuffix[/]addsuffix<now YYYY0MM0DD>]
\function get.calendar.link() [<linkTemplate>substitute[]]

\procedure addToCalendarActions()
<$action-listops $tiddler=<<currentTiddler>> $tags="[[In calendar]]"/>
<$wikify name="wikifiedText" text={{!!text}} output="html">
	<$action-sendmessage $message="tm-open-external-window" $param=<<get.calendar.link>> />
</$wikify>
\end addToCalendarActions

<$button tooltip={{$:/wilk/buttons/add-to-calendar!!description}} class=<<tv-config-toolbar-class>> actions=<<addToCalendarActions>> >
	<%if [<tv-config-toolbar-icons>match[yes]] %>
		{{$:/core/images/new-journal-button|1em|G}}
	<%endif%>
	<%if [<tv-config-toolbar-text>match[yes]] %>
		<span class=tc-btn-text>Add to calendar</span>
	<%endif%>
</$button>

3 Likes

This is great, thank you. It didn’t occur to me that in my version the wikification would happen every time the button is displayed.

I have discovered one issue of the current wikification method. The internal links are represented as <a href="#HelloThere">. When manually selecting and copying rendered tiddler, these links are (I assume) converted by the browser to the full form e.g. https://tiddlywiki.com/#HelloThere, and so they work outside of this wiki and point back to it.
When the wikify html output is directly used on a different site, e.g. in Google Calendar, the #HelloThere link gets interpreted by the browser as https://calendar.google.com/calendar/u/0/r#HelloThere, instead of pointing to the wiki it was taken from.

Does anyone have any idea to overcome this, other than running a search replace href="#href="https://url.of.current.wiki/# on the wikify output?

Have a look at https://tiddlywiki.com/#tv-filter-export-link%20Variable

1 Like

Thanks again. Adding

\procedure tv-filter-export-link() [encodeuricomponent[]encodeuricomponent[]addprefix[#]addprefix{$:/info/url/full}]

to the button solves the issue.

Hi Saq,
Can we use here a function instead of macro? This is because the content is filter expression.

So, instead of

\define tv-filter-export-link() [encodeuricomponent[]encodeuricomponent[]addsuffix[.html]]

Use

\function tv-filter-export-link() [encodeuricomponent[]encodeuricomponent[]addsuffix[.html]]

The core interprets tv-filter-export-link as a variable and the value of that variable is then invoked as a filter. As such, a procedure seems most appropriate, and assigning a function may not work correctly.

Thank you for your clarification!

Hey all, sorry for the late contribution, in case this has already been solved, but the following modification to @saqimtiaz’s procedures opens the edit event within the Google Calendar app on android, if it’s installed (or on browser otherwise). It may work on iOS as well, can we get someone to confirm?

\whitespace trim

\procedure linkTemplate() https://www.google.com/calendar/render?action=TEMPLATE&text=$(get.encoded.title)$&details=$(get.details)$&dates=$(get.dates)$
\function get.encoded.title() [<currentTiddler>encodeuricomponent[]]
\function get.details() [<wikifiedText>encodeuricomponent[]]
\function get.dates() [<now [YYYY0MM0DD]>addsuffix[T000000Z/]addsuffix<now [YYYY0MM0DD]>addsuffix[T235959Z]]
\function get.calendar.link() [<linkTemplate>substitute[]]

\procedure addToCalendarActions()
<$action-listops $tiddler=<<currentTiddler>> $tags="[[In calendar]]"/>
<$wikify name="wikifiedText" text={{!!text}} output="html">
	<$action-sendmessage $message="tm-open-external-window" $param=<<get.calendar.link>> />
</$wikify>
\end addToCalendarActions

<$button tooltip={{$:/wilk/buttons/add-to-calendar!!description}} class=<<tv-config-toolbar-class>> actions=<<addToCalendarActions>> >
	<%if [<tv-config-toolbar-icons>match[yes]] %>
		{{$:/core/images/new-journal-button|1em|G}}
	<%endif%>
	<%if [<tv-config-toolbar-text>match[yes]] %>
		<span class=tc-btn-text>Add to calendar</span>
	<%endif%>
</$button>

Thanks for the find, @vilc!

1 Like

And, if you’re a streams user and use it anything like I do, the following modification will, rather, set the title of the calendar event to the text of the currentTiddler and set its details to the contents of its stream-list:

\procedure linkTemplate() https://www.google.com/calendar/render?action=TEMPLATE&text=$(get.encoded.title)$&details=$(get.details)$&dates=$(get.dates)$
\function get.encoded.title() [<thisTiddler>get[text]encodeuricomponent[]]
\function get.dates() [<calendarDate>split[-]join[]addsuffix[/]append<calendarDate>split[-]join[]add[1]]
\function get.calendar.link() [<linkTemplate>substitute[]]
\function get.details() [<combinedText>encodeuricomponent[]]

\procedure addToCalendarActions()
<$action-listops $tiddler=<<thisTiddler>> $tags="[[In calendar]]"/>

<$vars thisTiddler=<<currentTiddler>>>
<$let 
    streamList={{{ [<thisTiddler>get[stream-list]] }}}
    calendarDate={{{ [<thisTiddler>get[calendar]] :else[<now [UTC]YYYY0MM0DD>] }}}
>
    <$set name="combinedText" value={{{ [enlist<streamList>get[text]join[

]] }}}>
        <$action-sendmessage $message="tm-open-external-window" $param=<<get.calendar.link>> />
    </$set>
</$let>
</$vars>
\end

Only problem I’m having is getting the stream-list contents wikified – anybody have any ideas?

Figured it out! Here is the code I’m currently using for the button, which is reflective of my workflow involving the streams plugin

\procedure linkTemplate() https://www.google.com/calendar/render?action=TEMPLATE&text=$(get.encoded.title)$&details=$(get.details)$&dates=$(get.dates)$&allday=true

\function get.encoded.title() [<plainTitle>encodeuricomponent[]]
\function get.dates() [<calendarDate>split[-]join[]addsuffix[/]append<calendarDate>split[-]join[]]
\function get.calendar.link() [<linkTemplate>substitute[]]
\function get.details() [<wikifiedText>encodeuricomponent[]]

\procedure addToCalendarActions()
<$action-listops $tiddler=<<thisTiddler>> $tags="[[In calendar]]"/>

<$vars thisTiddler=<<currentTiddler>>>
<$let 
    streamList={{{ [<thisTiddler>get[stream-list]] }}}
    calendarDate={{{ [<thisTiddler>get[calendar]] :else[<now [UTC]YYYY0MM0DD>] }}}
>
    <$wikify name="plainTitle" text={{{ [<thisTiddler>get[text]] }}} type="text/vnd.tiddlywiki">
        <$wikify name="wikifiedText" text={{{ [enlist<streamList>get[text]join[

]] }}} output="html">
            <$action-sendmessage $message="tm-open-external-window" $param=<<get.calendar.link>> />
        </$wikify>
    </$wikify>
</$let>
</$vars>
\end

It takes the text of the current tiddler as the title (and wikifies it as text/vnd.tiddlywiki" first so that it will remove reference brackets), and uses the text of the stream-list items as the details.

It also will set the date by default to the current date, but if you have a “calendar” value in the tiddler, it will use that date instead (I personally am using this with the datepicker)

image

I interact with this through through view conditions I have set up in the streams-row-body for items tagged “TODO” – the calendar button creates and opens the URI and the arrow opens the datepicker

image


The last problem I’m having is that, on the mobile app, it’s setting the end day as one day prior to the start day. This is pretty minor and doesn’t happen on the browser for some reason - - but would be very interested to figure out why it’s happening and how to resolve it if anyone has any suggestions.

1 Like

I have made a similar button a while ago, AFAIK it work properly on mobile: share

Here’s the wikitext I’m using:

<span title="Click to modify the event"><$macrocall
$name="details" 
label="""
{{$:/img/google agenda|10pt}}
<$let
utc-time="[split[:]join[]addsuffix[00000]]"
utc-date="[split[-]join[]]"
start-time={{!!start-time}}
start-date={{!!start-date}}
end-time={{{[{!!end-time}!is[blank]else<start-time>]}}}
end-date={{{[{!!end-date}!is[blank]else<start-date>]}}}
start={{{[<start-date>subfilter<utc-date>][<start-time>subfilter<utc-time>]+[join[T]]}}}
end={{{[<end-date>subfilter<utc-date>][<end-time>subfilter<utc-time>]+[join[T]]}}}
name={{!!name}}
description={{!!description}}
location={{!!location}}
source={{{[{$:/info/url/full}] [{!!title}format:titlelist[]] +[join[#:]]}}}
href={{{
="https://calendar.google.com/calendar/render?action=TEMPLATE"
="&text="=[<name>encodeuricomponent[]]
="&dates="=[<start>]="/"=[<end>]
="&details="
=[<description>encodeuricomponent[]]
=[[<hr><a href="]encodeuricomponent[]]
=[<source>encodeuricomponent[]]
=[[">🔗 Event page</a>]encodeuricomponent[]]
="&location="=[<location>encodeuricomponent[]]
="&sprop=website:"=[<source>encodeuricomponent[]]
="&sprop=name:"=[{$:/SiteTitle}encodeuricomponent[]]
+[join[]]
}}}
display-date-format="[UTC]DD  mmm YYYY at 0hh:0mm"
display-date={{{
[<start-date>subfilter<utc-date>] 
[<start-time>subfilter<utc-time>]
+[join[]format:date<display-date-format>lowercase[]] 
}}}
>
<a href=<<href>> >{{!!name}}, <<display-date>></a>
</$let>
<$list filter="[{!!description}!is[blank]]" variable="ignore">: {{!!description}}</$list>
"""
src="""
<p style="display:flex;justify-content:space-between;list-style:none;">
<span>Start:
<$edit-text type="date" field="start-date"/>
<$edit-text type="time"  field="start-time"/>
</span>
<span>End:
<$edit-text type="date" field="end-date" default={{!!start-date}}/>
<$edit-text type="time"  field="end-time" default={{!!start-time}}/>
</span>
</p>
<$edit field="name" placeholder="Name of the event" class="tc-edit-texteditor tc-edit-texteditor-body"/>
<$edit field="location" placeholder="Location" class="tc-edit-texteditor tc-edit-texteditor-body"/>
<<editor field:"description"  placeholder:"Description" tag:"textarea">>
"""
srcClass="alert border-secondary py-0" labelClass="badge badge-light"
/>