Michael Teeuw 8c0e7db494
Release 2.26.0 (#3319)
## [2.26.0] - 01-01-2024

Thanks to: @bnitkin, @bugsounet, @dependabot, @jkriegshauser,
@kaennchenstruggle, @KristjanESPERANTO and @Ybbet.

Special thanks to @khassel, @rejas and @sdetweil for taking over most
(if not all) of the work on this release as project collaborators. This
version would not be there without their effort. Thank you guys! You are
awesome!

This release also marks the latest release by Michael Teeuw. For more
info, please read the following post: [A New Chapter for MagicMirror:
The Community Takes the
Lead](https://forum.magicmirror.builders/topic/18329/a-new-chapter-for-magicmirror-the-community-takes-the-lead).

### Added

- Added update notification updater (for 3rd party modules)
- Added node 21 to the test matrix
- Added transform object to calendar:customEvents
- Added ESLint rules for jest (including jest/expect-expect and
jest/no-done-callback)

### Removed

- Removed Codecov workflow (not working anymore, other workflow
required) (#3107)
- Removed titleReplace from calendar, replaced + extended by
customEvents (backward compatibility included) (#3249)
- Removed failing unit test (#3254)
- Removed some unused variables

### Updated

- Update electron to v27 and update other dependencies as well as github
actions
- Update newsfeed: Use `html-to-text` instead of regex for transform
description
- Review ESLint config (#3269)
- Updated dependencies
- Clock module: optionally display current moon phase in addition to
rise/set times
- electron is now per default started without gpu, if needed it must be
enabled with new env var `ELECTRON_ENABLE_GPU=1` on startup (#3226)
- Replace prettier by stylistic in ESLint config to lint JavaScript (and
disable some rules for `config/config.js*` files)
- Update node-ical to v0.17.1 and fix tests

### Fixed

- Avoid fade out/in on updateDom when many calendars are used
- Fix the option eventClass on customEvents.
- Fix yr API version in locationforecast and sunrise call (#3227)
- Fix cloneObject() function to respect RegExp (#3237)
- Fix newsfeed module for feeds using "a10:updated" tag (#3238)
- Fix issue template (#3167)
- Fix #3256 filter out bad results from rrule.between
- Fix calendar events sometimes not respecting deleted events (#3250)
- Fix electron loadurl locally on Windows when address "0.0.0.0" (#2550)
- Fix updatanotification (update_helper.js): catch error if reponse is
not an JSON format (check PM2)
- Fix missing typeof in calendar module
- Fix style issues after prettier update
- Fix calendar test (#3291) by moving "Exdate check" from e2e to
electron to run on a Thursday
- Fix calendar config params `fetchInterval` and `excludedEvents` were
never used from single calendar config (#3297)
- Fix MM_PORT variable not used in electron and allow full path for
MM_CONFIG_FILE variable (#3302)

---------

Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Karsten Hassel <hassel@gmx.de>
Co-authored-by: Malte Hallström <46646495+SkySails@users.noreply.github.com>
Co-authored-by: Veeck <github@veeck.de>
Co-authored-by: veeck <michael@veeck.de>
Co-authored-by: dWoolridge <dwoolridge@charter.net>
Co-authored-by: Johan <jojjepersson@yahoo.se>
Co-authored-by: Dario Mratovich <dario_mratovich@hotmail.com>
Co-authored-by: Dario Mratovich <dario.mratovich@outlook.com>
Co-authored-by: Magnus <34011212+MagMar94@users.noreply.github.com>
Co-authored-by: Naveen <172697+naveensrinivasan@users.noreply.github.com>
Co-authored-by: buxxi <buxxi@omfilm.net>
Co-authored-by: Thomas Hirschberger <47733292+Tom-Hirschberger@users.noreply.github.com>
Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com>
Co-authored-by: Andrés Vanegas Jiménez <142350+angeldeejay@users.noreply.github.com>
Co-authored-by: Dave Child <dave@addedbytes.com>
Co-authored-by: grenagit <46225780+grenagit@users.noreply.github.com>
Co-authored-by: Grena <grena@grenabox.fr>
Co-authored-by: Magnus Marthinsen <magmar@online.no>
Co-authored-by: Patrick <psieg@users.noreply.github.com>
Co-authored-by: Piotr Rajnisz <56397164+rajniszp@users.noreply.github.com>
Co-authored-by: Suthep Yonphimai <tomzt@users.noreply.github.com>
Co-authored-by: CarJem Generations (Carter Wallace) <cwallacecs@gmail.com>
Co-authored-by: Nicholas Fogal <nfogal.misc@gmail.com>
Co-authored-by: JakeBinney <126349119+JakeBinney@users.noreply.github.com>
Co-authored-by: OWL4C <124401812+OWL4C@users.noreply.github.com>
Co-authored-by: Oscar Björkman <17575446+oscarb@users.noreply.github.com>
Co-authored-by: Ismar Slomic <ismar@slomic.no>
Co-authored-by: Jørgen Veum-Wahlberg <jorgen.wahlberg@amedia.no>
Co-authored-by: Eddie Hung <6740044+eddiehung@users.noreply.github.com>
Co-authored-by: Bugsounet - Cédric <github@bugsounet.fr>
Co-authored-by: bugsounet <bugsounet@bugsounet.fr>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Knapoc <Knapoc@users.noreply.github.com>
Co-authored-by: sam detweiler <sdetweil@gmail.com>
Co-authored-by: veeck <michael.veeck@nebenan.de>
Co-authored-by: Paranoid93 <6515818+Paranoid93@users.noreply.github.com>
Co-authored-by: NolanKingdon <27908974+NolanKingdon@users.noreply.github.com>
Co-authored-by: J. Kenzal Hunter <kenzal.hunter@gmail.com>
Co-authored-by: Teddy <teddy.payet@gmail.com>
Co-authored-by: TeddyStarinvest <teddy.payet@starinvest.com>
Co-authored-by: martingron <61826403+martingron@users.noreply.github.com>
Co-authored-by: dgoth <132394363+dgoth@users.noreply.github.com>
Co-authored-by: kaennchenstruggle <54073894+kaennchenstruggle@users.noreply.github.com>
Co-authored-by: jkriegshauser <jkriegshauser@gmail.com>
Co-authored-by: Ben Nitkin <ben@nitkin.net>
2024-01-01 15:38:08 +01:00

172 lines
5.7 KiB
JavaScript

/**
* A function to make HTTP requests via the server to avoid CORS-errors.
* @param {string} url the url to fetch from
* @param {string} type what contenttype to expect in the response, can be "json" or "xml"
* @param {boolean} useCorsProxy A flag to indicate
* @param {Array.<{name: string, value:string}>} requestHeaders the HTTP headers to send
* @param {Array.<string>} expectedResponseHeaders the expected HTTP headers to receive
* @returns {Promise} resolved when the fetch is done. The response headers is placed in a headers-property (provided the response does not already contain a headers-property).
*/
async function performWebRequest (url, type = "json", useCorsProxy = false, requestHeaders = undefined, expectedResponseHeaders = undefined) {
const request = {};
let requestUrl;
if (useCorsProxy) {
requestUrl = getCorsUrl(url, requestHeaders, expectedResponseHeaders);
} else {
requestUrl = url;
request.headers = getHeadersToSend(requestHeaders);
}
const response = await fetch(requestUrl, request);
const data = await response.text();
if (type === "xml") {
return new DOMParser().parseFromString(data, "text/html");
} else {
if (!data || !data.length > 0) return undefined;
const dataResponse = JSON.parse(data);
if (!dataResponse.headers) {
dataResponse.headers = getHeadersFromResponse(expectedResponseHeaders, response);
}
return dataResponse;
}
}
/**
* Gets a URL that will be used when calling the CORS-method on the server.
* @param {string} url the url to fetch from
* @param {Array.<{name: string, value:string}>} requestHeaders the HTTP headers to send
* @param {Array.<string>} expectedResponseHeaders the expected HTTP headers to receive
* @returns {string} to be used as URL when calling CORS-method on server.
*/
const getCorsUrl = function (url, requestHeaders, expectedResponseHeaders) {
if (!url || url.length < 1) {
throw new Error(`Invalid URL: ${url}`);
} else {
let corsUrl = `${location.protocol}//${location.host}/cors?`;
const requestHeaderString = getRequestHeaderString(requestHeaders);
if (requestHeaderString) corsUrl = `${corsUrl}sendheaders=${requestHeaderString}`;
const expectedResponseHeadersString = getExpectedResponseHeadersString(expectedResponseHeaders);
if (requestHeaderString && expectedResponseHeadersString) {
corsUrl = `${corsUrl}&expectedheaders=${expectedResponseHeadersString}`;
} else if (expectedResponseHeadersString) {
corsUrl = `${corsUrl}expectedheaders=${expectedResponseHeadersString}`;
}
if (requestHeaderString || expectedResponseHeadersString) {
return `${corsUrl}&url=${url}`;
}
return `${corsUrl}url=${url}`;
}
};
/**
* Gets the part of the CORS URL that represents the HTTP headers to send.
* @param {Array.<{name: string, value:string}>} requestHeaders the HTTP headers to send
* @returns {string} to be used as request-headers component in CORS URL.
*/
const getRequestHeaderString = function (requestHeaders) {
let requestHeaderString = "";
if (requestHeaders) {
for (const header of requestHeaders) {
if (requestHeaderString.length === 0) {
requestHeaderString = `${header.name}:${encodeURIComponent(header.value)}`;
} else {
requestHeaderString = `${requestHeaderString},${header.name}:${encodeURIComponent(header.value)}`;
}
}
return requestHeaderString;
}
return undefined;
};
/**
* Gets headers and values to attach to the web request.
* @param {Array.<{name: string, value:string}>} requestHeaders the HTTP headers to send
* @returns {object} An object specifying name and value of the headers.
*/
const getHeadersToSend = (requestHeaders) => {
const headersToSend = {};
if (requestHeaders) {
for (const header of requestHeaders) {
headersToSend[header.name] = header.value;
}
}
return headersToSend;
};
/**
* Gets the part of the CORS URL that represents the expected HTTP headers to receive.
* @param {Array.<string>} expectedResponseHeaders the expected HTTP headers to receive
* @returns {string} to be used as the expected HTTP-headers component in CORS URL.
*/
const getExpectedResponseHeadersString = function (expectedResponseHeaders) {
let expectedResponseHeadersString = "";
if (expectedResponseHeaders) {
for (const header of expectedResponseHeaders) {
if (expectedResponseHeadersString.length === 0) {
expectedResponseHeadersString = `${header}`;
} else {
expectedResponseHeadersString = `${expectedResponseHeadersString},${header}`;
}
}
return expectedResponseHeaders;
}
return undefined;
};
/**
* Gets the values for the expected headers from the response.
* @param {Array.<string>} expectedResponseHeaders the expected HTTP headers to receive
* @param {Response} response the HTTP response
* @returns {string} to be used as the expected HTTP-headers component in CORS URL.
*/
const getHeadersFromResponse = (expectedResponseHeaders, response) => {
const responseHeaders = [];
if (expectedResponseHeaders) {
for (const header of expectedResponseHeaders) {
const headerValue = response.headers.get(header);
responseHeaders.push({ name: header, value: headerValue });
}
}
return responseHeaders;
};
/**
* Format the time according to the config
* @param {object} config The config of the module
* @param {object} time time to format
* @returns {string} The formatted time string
*/
const formatTime = (config, time) => {
let date = moment(time);
if (config.timezone) {
date = date.tz(config.timezone);
}
if (config.timeFormat !== 24) {
if (config.showPeriod) {
if (config.showPeriodUpper) {
return date.format("h:mm A");
} else {
return date.format("h:mm a");
}
} else {
return date.format("h:mm");
}
}
return date.format("HH:mm");
};
if (typeof module !== "undefined") module.exports = {
performWebRequest,
formatTime
};