Is it possible to define custom day names, short day names, month names etc. in FullCalendar version 5? I believe most of the localization is now done natively using javascript's Intl object. But I need to define latin names, which are not to be found in the Intl object. Can it be done with a custom locale file or maybe upon initialization of the calendar by passing in a property? From my tests neither of the two are working but maybe I'm just not doing it right?
let calendar = new FullCalendar.Calendar(calendarEl, {
dayMaxEvents: true,
events: $events,
locale: 'la',
dayNames: ['Dies Solis','Dies Lunae','Dies Martis','Dies Mercurii','Dies Iovis','Dies Veneris','Dies Saturni'],
dayNamesShort: ['Sol','Lun','Mart','Merc','Iov','Ven','Sat']
});
and the locale file la.js :
FullCalendar.globalLocales.push(function () {
'use strict';
var la = {
code: "la",
week: {
dow: 1, // Monday is the first day of the week.
doy: 4 // The week that contains Jan 4th is the first week of the year.
},
buttonText: {
prev: "Prior",
next: "Prox",
today: "Hodie",
month: "Mensis",
week: "Hebdomada",
day: "Dies",
list: "Agenda"
},
weekText: "Hb",
allDayText: "Tota die",
moreLinkText: function(n) {
return "+alii " + n;
},
noEventsText: "Rei gestae non sunt"
};
return la;
}());
(which I am loading via a script tag: <script src='../lib/locales/la.js'></script>).
Some things are picked up from the locale file, for example I see hodie instead of today.
I've tried defining dayNames and dayNamesShort in the locale file but doesn't seem to work either.
Related
I am trying to set the description of a Google Calendar event to have a different selected presenter that is chosen in a rotatory manner (it is a slide deck that needs to be presented by a different person each week). The Python script specifying the rationale would work as the following
import datetime
people = ['Person 1', 'Person 2', 'Person 3']
year, week, day = datetime.date.today().isocalendar()
print(people[week_of_year % len(people)])
the resulting person from this calculation would show up in the calendar event description. Is there any tool to do that?
I was not able to find a way to create a recurrent event with different descriptions for each event.
However, I found a workaround for you using Events: patch. You first create an event using Events: insert, get the ID of the event you created with Events: list, and after that, then get the ID of the recurrences using Events: instances. Lastly, you use Events: patch to add the new description.
It might sound long, but I'm adding a sample with explanations on how to use it.
# impor the libraries that you will use for the calendar API
from __future__ import print_function
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# The scopes that we need to use based on the Google documentation
# I added the information in the reference at the end of the answer
SCOPES = ['https://www.googleapis.com/auth/calendar',
'https://www.googleapis.com/auth/calendar.events']
# creation of the credential, I use as a base the Google Documentation "Python quickstart"
def main():
creds = None
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
service = build('calendar', 'v3', credentials=creds)
# The list of people that will be use in each event
peoples = ['Person 1', 'Person 2', 'Person 3', 'Person 4']
number_week = 0
event = {
# Event title
'summary': 'Testing create description',
# A test description will be replaced later, so you can add anything you want to it.
'description': 'test',
# The start and end dates need to be in ISO-8601 date format
# and the time zone needs to be formatted as an IANA Time Zone Database name, e.g. "Europe/Zurich
'start': {
'dateTime': '2022-10-28T09:00:00-07:00',
'timeZone': 'America/Los_Angeles',
},
'end': {
'dateTime': '2022-10-28T17:00:00-07:00',
'timeZone': 'America/Los_Angeles',
},
'recurrence': [
# You can change the recurrence of the event
# for this example is a weekly event, and it has 4 recurrences
'RRULE:FREQ=WEEKLY;COUNT=4'
],
# The list of attendees
'attendees': [
{'email': 'test#email.xyz'},
{'email': 'test2#email.xyz'},
],
'reminders': {
'useDefault': False,
'overrides': [
{'method': 'email', 'minutes': 24 * 60},
{'method': 'popup', 'minutes': 10},
],
},
}
# We use insert to create the event
event = service.events().insert(calendarId='primary', body=event).execute()
# You can use the print in the next line to see the information about the event while you are testing
# print('Event created: %s' % (event.get('htmlLink')))
page_token = None
# Search for the event, using the method list and the event title q='Testing create description'
# you can use any other search key to get the event ID
events = service.events().list(calendarId='primary', pageToken=page_token, q='Testing create description').execute()
# get the ID of the event
for event in events['items']:
# print(event['id'])
id_event = event['id']
# Will search the ID of the recurrences of the event using the method instances
# e.g. xxxxxxxxxxxxxxxxx_20221028T160000Z
while True:
recurrences_for_events = service.events().instances(calendarId='primary', eventId=id_event,
pageToken=page_token).execute()
# run the dictionary of each iteration
for recurrence_instance in recurrences_for_events['items']:
id_to_update = recurrence_instance['id']
# I used the next print just to review the IDs of each event
# print(id_to_update)
# Create the new body of the event with the name of the people in the list of peoples
# instead of using year, week, day = datetime.date.today().isocalendar(). I use a simple counter to iterate on the list
body = {
'description': peoples[number_week],
}
# update the event description with patch
updated_event = service.events().patch(calendarId='primary', eventId=id_to_update, body=body).execute()
# print(updated_event['description'])
# next patch in case you have a large recurrence of the event
page_token = events.get('nextPageToken')
number_week += 1
# This will reset the counter, in case the number of people is less than the recurrences of the event
if number_week >= len(peoples):
number_week = 0
if not page_token:
break
except HttpError as error:
print('An error occurred: %s' % error)
if __name__ == '__main__':
main()
If the recurrent event has already been created, you only need to use the imports until the number_week = 0, and the part from:
events = service.events().list(calendarId='primary', pageToken=page_token, q='Testing create description').execute()
To the end, you might need to modify the for loop to only run once per recurrence.
Reference:
Python quickstart
Events: insert
Events: list
Events: instances
Events: patch
ISO 8601
I am trying to use datetimepicker and I have these functions to override date formatting. In other places we use momentjs for date time formatting.
There is my plunker example.
Date.parseDate = function (input, format) {
return moment(input, format).toDate();
};
Date.prototype.dateFormat = function (format) {
return moment(this).format(format);
};
Use momentjs formats for datetimepicker:
format: "DD/MM/YYYY H:mm:ss",
formatTime: 'H:mm',
formatDate: 'DD/MM/YYYY',
But when using moment the "highlighted days" function does not work.
How to make that "highlighted days" function were working?
Found solution:
$(document).ready(function() {
var myFormatter = {
parseDate: function(vDate, vFormat) {
return moment(vDate, vFormat).toDate();
},
guessDate: function(vDateStr, vFormat){
return moment(vDateStr, vFormat).toDate();
},
parseFormat: function(vChar, vDate){
return vDate; // date string (I guess)
},
formatDate: function(vChar, vDate) {
return moment(vChar).format(vDate);
},
};
jQuery.datetimepicker.**setDateFormatter**(myFormatter);
jQuery('#datetimepicker').datetimepicker({
timepicker: true,
// 'd/m/y'format is requared for datetimepicker days HIGHLIGHT function to work!!!
//Date, time formating: http://php.net/manual/en/function.date.php
step: 15,
/*
// momentJs formating
format: "d/m/Y H:i:s",
formatTime: "H:i",
formatDate: "d/m/Y",
*/
format: "DD/MM/YYYY H:mm:ss",
formatTime: 'H:mm',
formatDate: 'DD/MM/YYYY', //I need to use this format, but it works only when using "d/m/Y" - so somewhere the php date formater is still used..
highlightedDates: [
"01/09/2016,,xdsoft_highlighted_mint",
"02/09/2016,,xdsoft_highlighted_mint",
"03/09/2016,,xdsoft_highlighted_mint",
"06.09/2016",
"07.09.2016",
"08.09.2016",
"12.09.2016,Christmas Eve,xdsoft_highlighted_mint",
"13.09.2016,Christmas Day,xdsoft_highlighted_mint",
"14.09.2016,Christmas Day,xdsoft_highlighted_mint",
"26.09.2016,,xdsoft_highlighted_mint"
]
});
There is working plunker example
The main issues are is my case is browserify/gulp modules concatenation.. And that datetimepicker is undefined under jquery...
So trying to change building to WebPack....
I'm using the week view, but instead of showing 7 columns per slide I want to show three columns, is it possible to archive this?
I failed to see any related method on the official documentation: http://fullcalendar.io/docs/
Version 2.2.5+ of Full Calendar has this kind of customization built in.
You just need to do something like this:
views: {
agendaThreeDay: {
type: 'agenda',
duration: { days: 3 },
buttonText: '3 day'
},
defaultView: 'agendaThreeDay'
}
You can get more information on this from the document page here.
Pull the source, use this code (may need some additional change).
src/agenda/AgendaThreeDayView.js
fcViews.agendaThreeDay = AgendaThreeDayView;
function AgendaThreeDayView(a) {
AgendaView.call(this, a);
}
AgendaThreeDayView.prototype = createObject(AgendaView.prototype);
$.extend(AgendaThreeDayView.prototype, {
name: "agendaThreeDay",
incrementDate: function(a, b) {
return a.clone().stripTime().add(b, "days");
},
render: function(a) {
this.intervalStart = a.clone().stripTime();
this.intervalEnd = this.intervalStart.clone().add(3, "days");
this.start = this.skipHiddenDays(this.intervalStart);
this.end = this.skipHiddenDays(this.intervalEnd, -1, true);
this.title = this.calendar.formatRange(this.start, this.end.clone().subtract(1), this.opt("titleFormat"), " — ");
AgendaView.prototype.render.call(this, 3);
}
});
Edit:
Remembered that you need to add the file to lumbar.json
Look here for how to build: https://github.com/arshaw/fullcalendar/wiki/Contributing-Code
I'm using FullCalendar Beta2, and I set the AllDay flag to True.
The calendar still treats End Date as exclusive!
How can I make the End date inclusive?
Many thanks.
#ZooZ - According to the Beta 2 Upgrade docs, the end date is now exclusive:
all end dates are now exclusive. For example, if an all-day event ends
on a Thursday, the end date will be 00:00:00 on Friday. The 1.x
versions had some strange rules in regards to this. Things should be
much simpler now that exclusive end dates are used consistently
throughout the API. In addition, this behavior is more consistent with
other API's and formats, such as iCalendar.
Reference: http://arshaw.com/fullcalendar/wiki/Upgrading-to-2/
I would just add one to your end date calculation to work around this :)
You can hook into eventAfterAllRender and update a copy of the events and force the calendar to refresh.
In my example the modification only applies to events marked as allDay events (allDay:true). I only modifies a copy/clone of the events data so it only changes the displaying, not the actual data (I think - I need to test it better). I added the clone function but you can use something else if you like. I added the forceRendererToDisplay flag make it run only once.
Here is a fiddle: https://jsfiddle.net/a3q9c5tr/15/
function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}
$('#calendar1').fullCalendar({
forceRerenderToDisplay: true,
eventAfterAllRender: function(){
var startdatestr = this.options.events[0].start;
var enddatestr = this.options.events[0].end;
if(this.options.forceRerenderToDisplay == true){
var endDisplayDate = new Date(enddatestr);
endDisplayDate.setDate(endDisplayDate.getDate() + 1);
this.options.forceRerenderToDisplay = false;
var evs = clone(this.options.events);
for(var i in evs){
if(evs[i].allDay){
evs[0].end = new Date(endDisplayDate).toISOString().slice(0,10);
}
}
this.calendar.removeEvents();
this.calendar.addEventSource(evs);
this.calendar.rerenderEvents();
}
},
events:[
{start:'2016-04-03',end:'2016-04-05',title:'my event', allDay:true}
],
header: {
left: 'prev,next,today',
center: 'title',
right: 'month,agendaWeek,agendaDay',
allDay:true
}
});
I know this is kind of old now but with end dates being exclusive I found this workaround without having to add additional days.
first up is set display time to false this will make it so that the time is not shown on the events.
displayEventTime: false,
Then remove the allDay tag from your event and I used a foreach loop for my events which I pulled from DB.
$events=[
"start_date"=>"2020-01-01 00:00:00",
"end_date"=>"2020-01-04 00:00:00",
"title"=>"My Event",
]
events:[
<?php foreach ($events as $event):?>
<?php echo "{start:'".$event["start_date"]."',end:'".$event["end_date"]."',title:'".$event["title"]."'}},";?>
<?php endforeach;?>
],
Within the events part is where I have a foreach loop for the events. I will add
<?php $date = DateTime::createFromFormat("Y-m-d H:i:s", $event["end_date"]);
$date->setTime(0, 0);
// Add 23 hours
$date->add(new DateInterval('PT23H'));?>
so my final foreach loop looks like
events:[
<?php foreach ($events as $event):?>
<?php $date = DateTime::createFromFormat("Y-m-d H:i:s", $event["end_date"]);
$date->setTime(0, 0);
// Add 23 hours
$date->add(new DateInterval('PT23H'));?>
<?php echo "
{start:'".$event["start_date"]."',end:'".$date->format('Y-m-d H:i:s')."',
title:'".$event["title"]."'}},";?>
<?php endforeach;?>
],
so this has the foreach loop within the events. Then I create the date in a easy format to work with where I add the 23 hours and then echo out the date formatted within the event itself.
This then displays the end date as inclusive without having to adjust nextDayThreshold or having to add days before adding a date to your Database.
By default if you enable the 'selectable' attribute it will allow you to click and drag and select several days. I would like to only allow the user to select a single day, not drag over multiple. Is there a way to have 'selectable' enabled, but disable the dragging feature that comes along with it?
If you want to limit highlight to a single day in agenda week view you can use following:
selectConstraint:{
start: '00:01',
end: '23:59',
},
if you want to limit the event you can use
eventConstraint:{
start: '00:00',
end: '24:00',
},
in the select callback, adding the following does the trick:
(fullcalendar 2 using moment.js)
if (start.add('days', 1).date() != end.date() )
$scope.eventCal.fullCalendar('unselect');
resources:
http://arshaw.com/fullcalendar/docs/selection/select_callback/
http://arshaw.com/fullcalendar/docs/selection/unselect_method/
You can select a single date or time by passing fullcalendar's 'select' method to the dayClick event listener:
$('#myCalendar').fullcalendar({
dayClick: function(date,jsEvent,view) {
$('#myCalendar').fullcalendar('select', date);
}
});
Note you will also need to fire the 'unselect' method on your next callback (or dayClick).
Why not use selectAllow?
Start by converting the start and end times to seconds. Compare that to the number of seconds in a day.
Working Solution Without Using Moment.js:
selectAllow: function (e) {
if (e.end.getTime() / 1000 - e.start.getTime() / 1000 <= 86400) {
return true;
}
}
This configuration setting worked for me on FullCalendar v5:
selectAllow: function(selectionInfo) {
let startDate = selectionInfo.start;
let endDate = selectionInfo.end;
endDate.setSeconds(endDate.getSeconds() - 1); // allow full day selection
if (startDate.getDate() === endDate.getDate()) {
return true;
} else {
return false;
}
}
simply :
selectAllow: function (selectInfo) {
return selectInfo.end.diff(selectInfo.start, 'days') == 1;
}
For me using the selectAllow option like this worked
selectAllow: function(selectionInfo) {
// Don't allow creation of events over more than 1 day
return moment(selectionInfo.start).utcOffset(false).isSame(moment(selectionInfo.end).subtract(1, 'second').utcOffset(false), 'day');
},
I used utcOffset(false) because for whatever reason it doesn't work reliably without it and I used subtract(1, 'second') because the end date is inclusive, so without it you can't select the end of the day
This will be executed only when the user selects a day
// ...
select: function(start, end){
if(moment(start._d).add(1, 'days').format('YYYY-MM-DD')==moment(end._d).format('YYYY-MM-DD')){
// just select one day
}
},
// ...
I could do this using validRange:
https://fullcalendar.io/docs/validRange
Not at this time: the range of selectable days can not be customized without modifying the source.