mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-06-30 04:59:32 +00:00
Merge pull request #2079 from rejas/issue_2023
Added config option to calendar-icons for recurring- and fullday-events
This commit is contained in:
commit
5c44f51d6d
@ -1,5 +1,4 @@
|
||||
package-lock.json
|
||||
/config/**/*
|
||||
/modules/default/calendar/vendor/ical.js/**/*
|
||||
/vendor/**/*
|
||||
!/vendor/vendor.js
|
||||
|
@ -14,6 +14,7 @@ _This release is scheduled to be released on 2020-10-01._
|
||||
- Test coverage with Istanbul, run it with `npm run test:coverage`.
|
||||
- Add lithuanian language.
|
||||
- Added support in weatherforecast for OpenWeather onecall API.
|
||||
- Added config option to calendar-icons for recurring- and fullday-events
|
||||
|
||||
### Updated
|
||||
|
||||
|
@ -205,7 +205,7 @@ Module.register("calendar", {
|
||||
eventWrapper.style.cssText = "color:" + this.colorForUrl(event.url);
|
||||
}
|
||||
|
||||
eventWrapper.className = "normal";
|
||||
eventWrapper.className = "normal event";
|
||||
|
||||
if (this.config.displaySymbol) {
|
||||
var symbolWrapper = document.createElement("td");
|
||||
@ -217,11 +217,7 @@ Module.register("calendar", {
|
||||
var symbolClass = this.symbolClassForUrl(event.url);
|
||||
symbolWrapper.className = "symbol align-right " + symbolClass;
|
||||
|
||||
var symbols = this.symbolsForUrl(event.url);
|
||||
if (typeof symbols === "string") {
|
||||
symbols = [symbols];
|
||||
}
|
||||
|
||||
var symbols = this.symbolsForEvent(event);
|
||||
for (var i = 0; i < symbols.length; i++) {
|
||||
var symbol = document.createElement("span");
|
||||
symbol.className = "fa fa-fw fa-" + symbols[i];
|
||||
@ -230,6 +226,7 @@ Module.register("calendar", {
|
||||
}
|
||||
symbolWrapper.appendChild(symbol);
|
||||
}
|
||||
|
||||
eventWrapper.appendChild(symbolWrapper);
|
||||
} else if (this.config.timeFormat === "dateheaders") {
|
||||
var blankCell = document.createElement("td");
|
||||
@ -559,15 +556,33 @@ Module.register("calendar", {
|
||||
},
|
||||
|
||||
/**
|
||||
* symbolsForUrl(url)
|
||||
* Retrieves the symbols for a specific url.
|
||||
* symbolsForEvent(event)
|
||||
* Retrieves the symbols for a specific event.
|
||||
*
|
||||
* argument url string - Url to look for.
|
||||
* argument event object - Event to look for.
|
||||
*
|
||||
* return string/array - The Symbols
|
||||
* return array - The Symbols
|
||||
*/
|
||||
symbolsForUrl: function (url) {
|
||||
return this.getCalendarProperty(url, "symbol", this.config.defaultSymbol);
|
||||
symbolsForEvent: function (event) {
|
||||
let symbols = this.getCalendarPropertyAsArray(event.url, "symbol", this.config.defaultSymbol);
|
||||
|
||||
if (event.recurringEvent === true && this.hasCalendarProperty(event.url, "recurringSymbol")) {
|
||||
symbols = this.mergeUnique(this.getCalendarPropertyAsArray(event.url, "recurringSymbol", this.config.defaultSymbol), symbols);
|
||||
}
|
||||
|
||||
if (event.fullDayEvent === true && this.hasCalendarProperty(event.url, "fullDaySymbol")) {
|
||||
symbols = this.mergeUnique(this.getCalendarPropertyAsArray(event.url, "fullDaySymbol", this.config.defaultSymbol), symbols);
|
||||
}
|
||||
|
||||
return symbols;
|
||||
},
|
||||
|
||||
mergeUnique: function (arr1, arr2) {
|
||||
return arr1.concat(
|
||||
arr2.filter(function (item) {
|
||||
return arr1.indexOf(item) === -1;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -659,6 +674,16 @@ Module.register("calendar", {
|
||||
return defaultValue;
|
||||
},
|
||||
|
||||
getCalendarPropertyAsArray: function (url, property, defaultValue) {
|
||||
let p = this.getCalendarProperty(url, property, defaultValue);
|
||||
if (!(p instanceof Array)) p = [p];
|
||||
return p;
|
||||
},
|
||||
|
||||
hasCalendarProperty: function (url, property) {
|
||||
return !!this.getCalendarProperty(url, property, undefined);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shortens a string if it's longer than maxLength and add a ellipsis to the end
|
||||
*
|
||||
@ -756,7 +781,7 @@ Module.register("calendar", {
|
||||
var calendar = this.calendarData[url];
|
||||
for (var e in calendar) {
|
||||
var event = cloneObject(calendar[e]);
|
||||
event.symbol = this.symbolsForUrl(url);
|
||||
event.symbol = this.symbolsForEvent(event);
|
||||
event.calendarName = this.calendarNameForUrl(url);
|
||||
event.color = this.colorForUrl(url);
|
||||
delete event.url;
|
||||
|
@ -9,7 +9,7 @@ const ical = require("ical");
|
||||
const moment = require("moment");
|
||||
const request = require("request");
|
||||
|
||||
const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNumberOfDays, auth, includePastEvents) {
|
||||
const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, includePastEvents) {
|
||||
const self = this;
|
||||
|
||||
let reloadTimer = null;
|
||||
@ -254,6 +254,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNu
|
||||
startDate: startDate.format("x"),
|
||||
endDate: endDate.format("x"),
|
||||
fullDayEvent: isFullDayEvent(event),
|
||||
recurringEvent: true,
|
||||
class: event.class,
|
||||
firstYear: event.start.getFullYear(),
|
||||
location: location,
|
||||
@ -317,7 +318,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumNu
|
||||
return a.startDate - b.startDate;
|
||||
});
|
||||
|
||||
events = newEvents;
|
||||
events = newEvents.slice(0, maximumEntries);
|
||||
|
||||
self.broadcastEvents();
|
||||
scheduleTimer();
|
||||
|
@ -20,7 +20,7 @@ module.exports = NodeHelper.create({
|
||||
// Override socketNotificationReceived method.
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "ADD_CALENDAR") {
|
||||
this.createFetcher(payload.url, payload.fetchInterval, payload.excludedEvents, payload.maximumNumberOfDays, payload.auth, payload.broadcastPastEvents, payload.id);
|
||||
this.createFetcher(payload.url, payload.fetchInterval, payload.excludedEvents, payload.maximumEntries, payload.maximumNumberOfDays, payload.auth, payload.broadcastPastEvents, payload.id);
|
||||
}
|
||||
},
|
||||
|
||||
@ -31,7 +31,7 @@ module.exports = NodeHelper.create({
|
||||
* attribute url string - URL of the news feed.
|
||||
* attribute reloadInterval number - Reload interval in milliseconds.
|
||||
*/
|
||||
createFetcher: function (url, fetchInterval, excludedEvents, maximumNumberOfDays, auth, broadcastPastEvents, identifier) {
|
||||
createFetcher: function (url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents, identifier) {
|
||||
var self = this;
|
||||
|
||||
if (!validUrl.isUri(url)) {
|
||||
@ -42,7 +42,7 @@ module.exports = NodeHelper.create({
|
||||
var fetcher;
|
||||
if (typeof self.fetchers[identifier + url] === "undefined") {
|
||||
Log.log("Create new calendar fetcher for url: " + url + " - Interval: " + fetchInterval);
|
||||
fetcher = new CalendarFetcher(url, fetchInterval, excludedEvents, maximumNumberOfDays, auth, broadcastPastEvents);
|
||||
fetcher = new CalendarFetcher(url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents);
|
||||
|
||||
fetcher.onReceive(function (fetcher) {
|
||||
self.sendSocketNotification("CALENDAR_EVENTS", {
|
||||
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
// Escaped
|
||||
"FOO\"BAR": "Today",
|
||||
|
||||
/*
|
||||
* The following lines
|
||||
* represent cardinal directions
|
||||
*/
|
||||
"N": "N",
|
||||
"E": "E",
|
||||
"S": "S",
|
||||
"W": "W"
|
||||
}
|
56
tests/configs/data/calendar_test_icons.ics
Normal file
56
tests/configs/data/calendar_test_icons.ics
Normal file
@ -0,0 +1,56 @@
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//ical.marudot.com//iCal Event Maker
|
||||
X-WR-CALNAME:TestEvents
|
||||
NAME:TestEvents
|
||||
CALSCALE:GREGORIAN
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Europe/Berlin
|
||||
TZURL:http://tzurl.org/zoneinfo-outlook/Europe/Berlin
|
||||
X-LIC-LOCATION:Europe/Berlin
|
||||
BEGIN:DAYLIGHT
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
TZNAME:CEST
|
||||
DTSTART:19700329T020000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||
END:DAYLIGHT
|
||||
BEGIN:STANDARD
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
TZNAME:CET
|
||||
DTSTART:19701025T030000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||
END:STANDARD
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
DTSTAMP:20200719T094531Z
|
||||
UID:20200719T094531Z-1871115387@marudot.com
|
||||
DTSTART;TZID=Europe/Berlin:20300101T120000
|
||||
DTEND;TZID=Europe/Berlin:20300101T130000
|
||||
SUMMARY:TestEvent
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTAMP:20200719T094531Z
|
||||
UID:20200719T094531Z-1929725136@marudot.com
|
||||
DTSTART;TZID=Europe/Berlin:20300701T120000
|
||||
RRULE:FREQ=YEARLY;BYMONTH=7;BYMONTHDAY=1
|
||||
DTEND;TZID=Europe/Berlin:20300701T130000
|
||||
SUMMARY:TestEventRepeat
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTAMP:20200719T094531Z
|
||||
UID:20200719T094531Z-371801474@marudot.com
|
||||
DTSTART;VALUE=DATE:20300401
|
||||
DTEND;VALUE=DATE:20300402
|
||||
SUMMARY:TestEventDay
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTAMP:20200719T094531Z
|
||||
UID:20200719T094531Z-133401084@marudot.com
|
||||
DTSTART;VALUE=DATE:20301001
|
||||
RRULE:FREQ=YEARLY;BYMONTH=10;BYMONTHDAY=1
|
||||
DTEND;VALUE=DATE:20301002
|
||||
SUMMARY:TestEventRepeatDay
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
41
tests/configs/modules/calendar/custom.js
Normal file
41
tests/configs/modules/calendar/custom.js
Normal file
@ -0,0 +1,41 @@
|
||||
/* Magic Mirror Test config custom calendar
|
||||
*
|
||||
* MIT Licensed.
|
||||
*/
|
||||
let config = {
|
||||
port: 8080,
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 12,
|
||||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "calendar",
|
||||
position: "bottom_bar",
|
||||
config: {
|
||||
calendars: [
|
||||
{
|
||||
symbol: "birthday-cake",
|
||||
fullDaySymbol: "calendar-day",
|
||||
recurringSymbol: "undo",
|
||||
maximumEntries: 4,
|
||||
maximumNumberOfDays: 10000,
|
||||
url: "http://localhost:8080/tests/configs/data/calendar_test_icons.ics"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
const helpers = require("../global-setup");
|
||||
const serverBasicAuth = require("../../servers/basic-auth.js");
|
||||
const expect = require("chai").expect;
|
||||
|
||||
const describe = global.describe;
|
||||
const it = global.it;
|
||||
@ -31,8 +32,47 @@ describe("Calendar module", function () {
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/calendar/default.js";
|
||||
});
|
||||
|
||||
it("Should return TestEvents", function () {
|
||||
return app.client.waitUntilTextExists(".calendar", "TestEvent", 10000);
|
||||
it("should show the default maximumEntries of 10", async () => {
|
||||
await app.client.waitUntilTextExists(".calendar", "TestEvent", 10000);
|
||||
const events = await app.client.$$(".calendar .event");
|
||||
return expect(events.length).equals(10);
|
||||
});
|
||||
|
||||
it("should show the default calendar symbol in each event", async () => {
|
||||
await app.client.waitUntilTextExists(".calendar", "TestEvent", 10000);
|
||||
const icons = await app.client.$$(".calendar .event .fa-calendar");
|
||||
return expect(icons.length).not.equals(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Custom configuration", function () {
|
||||
before(function () {
|
||||
// Set config sample for use in test
|
||||
process.env.MM_CONFIG_FILE = "tests/configs/modules/calendar/custom.js";
|
||||
});
|
||||
|
||||
it("should show the custom maximumEntries of 4", async () => {
|
||||
await app.client.waitUntilTextExists(".calendar", "TestEvent", 10000);
|
||||
const events = await app.client.$$(".calendar .event");
|
||||
return expect(events.length).equals(4);
|
||||
});
|
||||
|
||||
it("should show the custom calendar symbol in each event", async () => {
|
||||
await app.client.waitUntilTextExists(".calendar", "TestEvent", 10000);
|
||||
const icons = await app.client.$$(".calendar .event .fa-birthday-cake");
|
||||
return expect(icons.length).equals(4);
|
||||
});
|
||||
|
||||
it("should show two custom icons for repeating events", async () => {
|
||||
await app.client.waitUntilTextExists(".calendar", "TestEventRepeat", 10000);
|
||||
const icons = await app.client.$$(".calendar .event .fa-undo");
|
||||
return expect(icons.length).equals(2);
|
||||
});
|
||||
|
||||
it("should show two custom icons for day events", async () => {
|
||||
await app.client.waitUntilTextExists(".calendar", "TestEventDay", 10000);
|
||||
const icons = await app.client.$$(".calendar .event .fa-calendar-day");
|
||||
return expect(icons.length).equals(2);
|
||||
});
|
||||
});
|
||||
|
||||
@ -47,7 +87,7 @@ describe("Calendar module", function () {
|
||||
serverBasicAuth.close(done());
|
||||
});
|
||||
|
||||
it("Should return TestEvents", function () {
|
||||
it("should return TestEvents", function () {
|
||||
return app.client.waitUntilTextExists(".calendar", "TestEvent", 10000);
|
||||
});
|
||||
});
|
||||
@ -63,7 +103,7 @@ describe("Calendar module", function () {
|
||||
serverBasicAuth.close(done());
|
||||
});
|
||||
|
||||
it("Should return TestEvents", function () {
|
||||
it("should return TestEvents", function () {
|
||||
return app.client.waitUntilTextExists(".calendar", "TestEvent", 10000);
|
||||
});
|
||||
});
|
||||
@ -79,7 +119,7 @@ describe("Calendar module", function () {
|
||||
serverBasicAuth.close(done());
|
||||
});
|
||||
|
||||
it("Should return TestEvents", function () {
|
||||
it("should return TestEvents", function () {
|
||||
return app.client.waitUntilTextExists(".calendar", "TestEvent", 10000);
|
||||
});
|
||||
});
|
||||
@ -95,7 +135,7 @@ describe("Calendar module", function () {
|
||||
serverBasicAuth.close(done());
|
||||
});
|
||||
|
||||
it("Should return No upcoming events", function () {
|
||||
it("should return No upcoming events", function () {
|
||||
return app.client.waitUntilTextExists(".calendar", "No upcoming events.", 10000);
|
||||
});
|
||||
});
|
||||
|
@ -32,54 +32,54 @@ describe("Functions into modules/default/calendar/calendar.js", function () {
|
||||
});
|
||||
|
||||
describe("getLocaleSpecification", function () {
|
||||
it("Should return a valid moment.LocaleSpecification for a 12-hour format", function () {
|
||||
it("should return a valid moment.LocaleSpecification for a 12-hour format", function () {
|
||||
expect(Module.definitions.calendar.getLocaleSpecification(12)).to.deep.equal({ longDateFormat: { LT: "h:mm A" } });
|
||||
});
|
||||
|
||||
it("Should return a valid moment.LocaleSpecification for a 24-hour format", function () {
|
||||
it("should return a valid moment.LocaleSpecification for a 24-hour format", function () {
|
||||
expect(Module.definitions.calendar.getLocaleSpecification(24)).to.deep.equal({ longDateFormat: { LT: "HH:mm" } });
|
||||
});
|
||||
|
||||
it("Should return the current system locale when called without timeFormat number", function () {
|
||||
it("should return the current system locale when called without timeFormat number", function () {
|
||||
expect(Module.definitions.calendar.getLocaleSpecification()).to.deep.equal({ longDateFormat: { LT: moment.localeData().longDateFormat("LT") } });
|
||||
});
|
||||
|
||||
it("Should return a 12-hour longDateFormat when using the 'en' locale", function () {
|
||||
it("should return a 12-hour longDateFormat when using the 'en' locale", function () {
|
||||
var localeBackup = moment.locale();
|
||||
moment.locale("en");
|
||||
expect(Module.definitions.calendar.getLocaleSpecification()).to.deep.equal({ longDateFormat: { LT: "h:mm A" } });
|
||||
moment.locale(localeBackup);
|
||||
});
|
||||
|
||||
it("Should return a 12-hour longDateFormat when using the 'au' locale", function () {
|
||||
it("should return a 12-hour longDateFormat when using the 'au' locale", function () {
|
||||
var localeBackup = moment.locale();
|
||||
moment.locale("au");
|
||||
expect(Module.definitions.calendar.getLocaleSpecification()).to.deep.equal({ longDateFormat: { LT: "h:mm A" } });
|
||||
moment.locale(localeBackup);
|
||||
});
|
||||
|
||||
it("Should return a 12-hour longDateFormat when using the 'eg' locale", function () {
|
||||
it("should return a 12-hour longDateFormat when using the 'eg' locale", function () {
|
||||
var localeBackup = moment.locale();
|
||||
moment.locale("eg");
|
||||
expect(Module.definitions.calendar.getLocaleSpecification()).to.deep.equal({ longDateFormat: { LT: "h:mm A" } });
|
||||
moment.locale(localeBackup);
|
||||
});
|
||||
|
||||
it("Should return a 24-hour longDateFormat when using the 'nl' locale", function () {
|
||||
it("should return a 24-hour longDateFormat when using the 'nl' locale", function () {
|
||||
var localeBackup = moment.locale();
|
||||
moment.locale("nl");
|
||||
expect(Module.definitions.calendar.getLocaleSpecification()).to.deep.equal({ longDateFormat: { LT: "HH:mm" } });
|
||||
moment.locale(localeBackup);
|
||||
});
|
||||
|
||||
it("Should return a 24-hour longDateFormat when using the 'fr' locale", function () {
|
||||
it("should return a 24-hour longDateFormat when using the 'fr' locale", function () {
|
||||
var localeBackup = moment.locale();
|
||||
moment.locale("fr");
|
||||
expect(Module.definitions.calendar.getLocaleSpecification()).to.deep.equal({ longDateFormat: { LT: "HH:mm" } });
|
||||
moment.locale(localeBackup);
|
||||
});
|
||||
|
||||
it("Should return a 24-hour longDateFormat when using the 'uk' locale", function () {
|
||||
it("should return a 24-hour longDateFormat when using the 'uk' locale", function () {
|
||||
var localeBackup = moment.locale();
|
||||
moment.locale("uk");
|
||||
expect(Module.definitions.calendar.getLocaleSpecification()).to.deep.equal({ longDateFormat: { LT: "HH:mm" } });
|
||||
|
Loading…
x
Reference in New Issue
Block a user