Skip to content

Commit

Permalink
Merge pull request #2048 from rejas/issue_1926
Browse files Browse the repository at this point in the history
Updated ical library to latest version
  • Loading branch information
MichMich authored Jun 22, 2020
2 parents d328250 + 9831f81 commit 3b442d4
Show file tree
Hide file tree
Showing 31 changed files with 752 additions and 4,692 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ _This release is scheduled to be released on 2020-07-01._
- Fix the use of "maxNumberOfDays" in the module "weatherforecast" [#2018](https://github.com/MichMich/MagicMirror/issues/2018)
- Throw error when check_config fails [#1928](https://github.com/MichMich/MagicMirror/issues/1928)
- Bug fix related to 'maxEntries' not displaying Calendar events. [#2050](https://github.com/MichMich/MagicMirror/issues/2050)
- Updated ical library to latest version [#1926](https://github.com/MichMich/MagicMirror/issues/1926)

## [2.11.0] - 2020-04-01

Expand Down
167 changes: 83 additions & 84 deletions modules/default/calendar/calendarfetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,28 @@
* MIT Licensed.
*/
const Log = require("../../../js/logger.js");
const ical = require("./vendor/ical.js");
const ical = require("ical");
const moment = require("moment");
const request = require("request");

var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumberOfDays, auth, includePastEvents) {
var self = this;
const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumberOfDays, auth, includePastEvents) {
const self = this;

var reloadTimer = null;
var events = [];
let reloadTimer = null;
let events = [];

var fetchFailedCallback = function () {};
var eventsReceivedCallback = function () {};
let fetchFailedCallback = function () {};
let eventsReceivedCallback = function () {};

/* fetchCalendar()
* Initiates calendar fetch.
*/
var fetchCalendar = function () {
const fetchCalendar = function () {
clearTimeout(reloadTimer);
reloadTimer = null;

var nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
var opts = {
const nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
const opts = {
headers: {
"User-Agent": "Mozilla/5.0 (Node.js " + nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)"
},
Expand All @@ -40,63 +41,62 @@ var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumb
} else {
opts.auth = {
user: auth.user,
pass: auth.pass
pass: auth.pass,
sendImmediately: auth.method !== "digest"
};

if (auth.method === "digest") {
opts.auth.sendImmediately = false;
} else {
opts.auth.sendImmediately = true;
}
}
}

ical.fromURL(url, opts, function (err, data) {
request(url, opts, function (err, r, requestData) {
if (err) {
fetchFailedCallback(self, err);
scheduleTimer();
return;
} else if (r.statusCode !== 200) {
fetchFailedCallback(self, r.statusCode + ": " + r.statusMessage);
scheduleTimer();
return;
}

var newEvents = [];
const data = ical.parseICS(requestData);
const newEvents = [];

// limitFunction doesn't do much limiting, see comment re: the dates array in rrule section below as to why we need to do the filtering ourselves
var limitFunction = function (date, i) {
const limitFunction = function (date, i) {
return true;
};

var eventDate = function (event, time) {
const eventDate = function (event, time) {
return event[time].length === 8 ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time]));
};

for (var e in data) {
var event = data[e];
var now = new Date();
var today = moment().startOf("day").toDate();
var future = moment().startOf("day").add(maximumNumberOfDays, "days").subtract(1, "seconds").toDate(); // Subtract 1 second so that events that start on the middle of the night will not repeat.
var past = today;
Object.entries(data).forEach(([key, event]) => {
const now = new Date();
const today = moment().startOf("day").toDate();
const future = moment().startOf("day").add(maximumNumberOfDays, "days").subtract(1, "seconds").toDate(); // Subtract 1 second so that events that start on the middle of the night will not repeat.
let past = today;

if (includePastEvents) {
past = moment().startOf("day").subtract(maximumNumberOfDays, "days").toDate();
}

// FIXME: Ugly fix to solve the facebook birthday issue.
// Otherwise, the recurring events only show the birthday for next year.
var isFacebookBirthday = false;
let isFacebookBirthday = false;
if (typeof event.uid !== "undefined") {
if (event.uid.indexOf("@facebook.com") !== -1) {
isFacebookBirthday = true;
}
}

if (event.type === "VEVENT") {
var startDate = eventDate(event, "start");
var endDate;
let startDate = eventDate(event, "start");
let endDate;

if (typeof event.end !== "undefined") {
endDate = eventDate(event, "end");
} else if (typeof event.duration !== "undefined") {
var dur = moment.duration(event.duration);
endDate = startDate.clone().add(dur);
endDate = startDate.clone().add(moment.duration(event.duration));
} else {
if (!isFacebookBirthday) {
endDate = startDate;
Expand All @@ -105,20 +105,20 @@ var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumb
}
}

// calculate the duration f the event for use with recurring events.
var duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x"));
// calculate the duration of the event for use with recurring events.
let duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x"));

if (event.start.length === 8) {
startDate = startDate.startOf("day");
}

var title = getTitleFromEvent(event);
const title = getTitleFromEvent(event);

var excluded = false,
let excluded = false,
dateFilter = null;

for (var f in excludedEvents) {
var filter = excludedEvents[f],
for (let f in excludedEvents) {
let filter = excludedEvents[f],
testTitle = title.toLowerCase(),
until = null,
useRegex = false,
Expand Down Expand Up @@ -161,16 +161,19 @@ var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumb
}

if (excluded) {
continue;
return;
}

var location = event.location || false;
var geo = event.geo || false;
var description = event.description || false;
const location = event.location || false;
const geo = event.geo || false;
const description = event.description || false;

if (typeof event.rrule !== "undefined" && event.rrule !== null && !isFacebookBirthday) {
var rule = event.rrule;
var addedEvents = 0;
const rule = event.rrule;
let addedEvents = 0;

const pastMoment = moment(past);
const futureMoment = moment(future);

// can cause problems with e.g. birthdays before 1900
if ((rule.options && rule.origOptions && rule.origOptions.dtstart && rule.origOptions.dtstart.getFullYear() < 1900) || (rule.options && rule.options.dtstart && rule.options.dtstart.getFullYear() < 1900)) {
Expand All @@ -179,28 +182,24 @@ var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumb
}

// For recurring events, get the set of start dates that fall within the range
// of dates we"re looking for.
// of dates we're looking for.
// kblankenship1989 - to fix issue #1798, converting all dates to locale time first, then converting back to UTC time
var pastLocal = moment(past).subtract(past.getTimezoneOffset(), "minutes").toDate();
var futureLocal = moment(future).subtract(future.getTimezoneOffset(), "minutes").toDate();
var datesLocal = rule.between(pastLocal, futureLocal, true, limitFunction);
var dates = datesLocal.map(function (dateLocal) {
var date = moment(dateLocal).add(dateLocal.getTimezoneOffset(), "minutes").toDate();
return date;
const pastLocal = pastMoment.subtract(past.getTimezoneOffset(), "minutes").toDate();
const futureLocal = futureMoment.subtract(future.getTimezoneOffset(), "minutes").toDate();
const datesLocal = rule.between(pastLocal, futureLocal, true, limitFunction);
const dates = datesLocal.map(function (dateLocal) {
return moment(dateLocal).add(dateLocal.getTimezoneOffset(), "minutes").toDate();
});

// The "dates" array contains the set of dates within our desired date range range that are valid
// for the recurrence rule. *However*, it"s possible for us to have a specific recurrence that
// for the recurrence rule. *However*, it's possible for us to have a specific recurrence that
// had its date changed from outside the range to inside the range. For the time being,
// we"ll handle this by adding *all* recurrence entries into the set of dates that we check,
// because the logic below will filter out any recurrences that don"t actually belong within
// we'll handle this by adding *all* recurrence entries into the set of dates that we check,
// because the logic below will filter out any recurrences that don't actually belong within
// our display range.
// Would be great if there was a better way to handle this.
if (event.recurrences !== undefined) {
var pastMoment = moment(past);
var futureMoment = moment(future);

for (var r in event.recurrences) {
for (let r in event.recurrences) {
// Only add dates that weren't already in the range we added from the rrule so that
// we don"t double-add those events.
if (moment(new Date(r)).isBetween(pastMoment, futureMoment) !== true) {
Expand All @@ -210,25 +209,25 @@ var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumb
}

// Loop through the set of date entries to see which recurrences should be added to our event list.
for (var d in dates) {
var date = dates[d];
for (let d in dates) {
const date = dates[d];
// ical.js started returning recurrences and exdates as ISOStrings without time information.
// .toISOString().substring(0,10) is the method they use to calculate keys, so we'll do the same
// (see https://github.com/peterbraden/ical.js/pull/84 )
var dateKey = date.toISOString().substring(0, 10);
var curEvent = event;
var showRecurrence = true;
const dateKey = date.toISOString().substring(0, 10);
let curEvent = event;
let showRecurrence = true;

startDate = moment(date);

// For each date that we"re checking, it"s possible that there is a recurrence override for that one day.
// For each date that we're checking, it's possible that there is a recurrence override for that one day.
if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) {
// We found an override, so for this recurrence, use a potentially different title, start date, and duration.
curEvent = curEvent.recurrences[dateKey];
startDate = moment(curEvent.start);
duration = parseInt(moment(curEvent.end).format("x")) - parseInt(startDate.format("x"));
}
// If there"s no recurrence override, check for an exception date. Exception dates represent exceptions to the rule.
// If there's no recurrence override, check for an exception date. Exception dates represent exceptions to the rule.
else if (curEvent.exdate !== undefined && curEvent.exdate[dateKey] !== undefined) {
// This date is an exception date, which means we should skip it in the recurrence pattern.
showRecurrence = false;
Expand All @@ -239,7 +238,7 @@ var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumb
endDate = endDate.endOf("day");
}

var recurrenceTitle = getTitleFromEvent(curEvent);
const recurrenceTitle = getTitleFromEvent(curEvent);

// If this recurrence ends before the start of the date range, or starts after the end of the date range, don"t add
// it to the event list.
Expand Down Expand Up @@ -269,32 +268,32 @@ var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumb
// end recurring event parsing
} else {
// Single event.
var fullDayEvent = isFacebookBirthday ? true : isFullDayEvent(event);
const fullDayEvent = isFacebookBirthday ? true : isFullDayEvent(event);

if (includePastEvents) {
// Past event is too far in the past, so skip.
if (endDate < past) {
continue;
return;
}
} else {
// It's not a fullday event, and it is in the past, so skip.
if (!fullDayEvent && endDate < new Date()) {
continue;
return;
}

// It's a fullday event, and it is before today, So skip.
if (fullDayEvent && endDate <= today) {
continue;
return;
}
}

// It exceeds the maximumNumberOfDays limit, so skip.
if (startDate > future) {
continue;
return;
}

if (timeFilterApplies(now, endDate, dateFilter)) {
continue;
return;
}

// Adjust start date so multiple day events will be displayed as happening today even though they started some days ago already
Expand All @@ -315,7 +314,7 @@ var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumb
});
}
}
}
});

newEvents.sort(function (a, b) {
return a.startDate - b.startDate;
Expand All @@ -331,7 +330,7 @@ var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumb
/* scheduleTimer()
* Schedule the timer for the next update.
*/
var scheduleTimer = function () {
const scheduleTimer = function () {
clearTimeout(reloadTimer);
reloadTimer = setTimeout(function () {
fetchCalendar();
Expand All @@ -345,14 +344,14 @@ var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumb
*
* return bool - The event is a fullday event.
*/
var isFullDayEvent = function (event) {
const isFullDayEvent = function (event) {
if (event.start.length === 8 || event.start.dateOnly) {
return true;
}

var start = event.start || 0;
var startDate = new Date(start);
var end = event.end || 0;
const start = event.start || 0;
const startDate = new Date(start);
const end = event.end || 0;
if ((end - start) % (24 * 60 * 60 * 1000) === 0 && startDate.getHours() === 0 && startDate.getMinutes() === 0) {
// Is 24 hours, and starts on the middle of the night.
return true;
Expand All @@ -370,11 +369,11 @@ var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumb
*
* return bool - The event should be filtered out
*/
var timeFilterApplies = function (now, endDate, filter) {
const timeFilterApplies = function (now, endDate, filter) {
if (filter) {
var until = filter.split(" "),
const until = filter.split(" "),
value = parseInt(until[0]),
increment = until[1].slice("-1") === "s" ? until[1] : until[1] + "s", // Massage the data for moment js
increment = until[1].slice(-1) === "s" ? until[1] : until[1] + "s", // Massage the data for moment js
filterUntil = moment(endDate.format()).subtract(value, increment);

return now < filterUntil.format("x");
Expand All @@ -390,8 +389,8 @@ var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumb
*
* return string - The title of the event, or "Event" if no title is found.
*/
var getTitleFromEvent = function (event) {
var title = "Event";
const getTitleFromEvent = function (event) {
let title = "Event";
if (event.summary) {
title = typeof event.summary.val !== "undefined" ? event.summary.val : event.summary;
} else if (event.description) {
Expand All @@ -401,7 +400,7 @@ var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumb
return title;
};

var testTitleByFilter = function (title, filter, useRegex, regexFlags) {
const testTitleByFilter = function (title, filter, useRegex, regexFlags) {
if (useRegex) {
// Assume if leading slash, there is also trailing slash
if (filter[0] === "/") {
Expand Down
Loading

0 comments on commit 3b442d4

Please sign in to comment.