From 2a6e2aacdc3ffb093c2027f475255ff31577aa5f Mon Sep 17 00:00:00 2001 From: kaennchenstruggle <54073894+kaennchenstruggle@users.noreply.github.com> Date: Wed, 1 Nov 2023 00:07:56 +0100 Subject: [PATCH] Calendar translate (#3249) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hello and thank you for wanting to contribute to the MagicMirror² project **Please make sure that you have followed these 4 rules before submitting your Pull Request:** > 1. Base your pull requests against the `develop` branch. DONE ;D > 2. Include these infos in the description: > - Does the pull request solve a **related** issue? NO > - What does the pull request accomplish? Use a list if needed. For calendar entries containing a year (e.g. DOB) in the title, the age can be calculated. Example before: ![grafik](https://github.com/MichMich/MagicMirror/assets/54073894/67ca65f4-24c3-46a8-bee8-0519e4bba3f5) after: ![grafik](https://github.com/MichMich/MagicMirror/assets/54073894/0b4af07d-d3d9-4644-a4a6-e8c402598208) Achieved by adding a new keyword `transform` to customEvents ``` customEvents: [ {keyword: 'Geburtstag', symbol: 'birthday-cake', color: 'Gold', transform: { search: '^([^\']*) \'(\\d{4})$' , replace: '$1 ($2.)', yearmatchgroup: 2}}, {keyword: 'in Hamburg', transform: { search: ' in Hamburg$' , replace: ''}} ], ``` and therewith obsoleting `titleReplace`; a backward compatibility part is already included. If `yearmatchgroup` is unset, behaviour is as in previous code (some additions to which RegExes are accepted, though) If `yearmatchgroup` is set, it is considered the RegEx match group id, which will be used for calculating the age. > - If it includes major visual changes please add screenshots. NO > 3. Please run `npm run lint:prettier` before submitting so that style issues are fixed. DONE > 4. Don't forget to add an entry about your changes to the CHANGELOG.md file. DONE > Thanks again and have a nice day! You too and if any questions, feel free to let me know. --------- Co-authored-by: veeck --- CHANGELOG.md | 2 ++ modules/default/calendar/calendar.js | 16 +++++++-- modules/default/calendar/calendarutils.js | 36 +++++++++++++------ .../default/calendar/calendar_utils_spec.js | 13 ++++--- 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59b52e71..9a170d33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,12 @@ _This release is scheduled to be released on 2024-01-01._ ### Added - Added node 21 to the test matrix +- Added transform object to calendar:customEvents ### Removed - Removed Codecov workflow (not working anymore, other workflow required) (#3107) +- Removed titleReplace from calendar, replaced + extended by customEvents (backward compatibility included) ### Updated diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index c923ee50..287a424a 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -155,6 +155,14 @@ Module.register("calendar", { this.addCalendar(calendar.url, calendar.auth, calendarConfig); }); + // for backward compatibility titleReplace + if (typeof this.config.titleReplace !== "undefined") { + Log.warn("Deprecation warning: Please consider upgrading your calendar titleReplace configuration to customEvents."); + for (const [titlesearchstr, titlereplacestr] of Object.entries(this.config.titleReplace)) { + this.config.customEvents.push({ keyword: ".*", transform: { search: titlesearchstr, replace: titlereplacestr } }); + } + } + this.selfUpdate(); }, @@ -326,11 +334,16 @@ Module.register("calendar", { } } - // Color events if custom color or eventClass are specified + var transformedTitle = event.title; + + // Color events if custom color or eventClass are specified, transform title if required if (this.config.customEvents.length > 0) { for (let ev in this.config.customEvents) { let needle = new RegExp(this.config.customEvents[ev].keyword, "gi"); if (needle.test(event.title)) { + if (typeof this.config.customEvents[ev].transform === "object") { + transformedTitle = CalendarUtils.titleTransform(transformedTitle, [this.config.customEvents[ev].transform]); + } if (typeof this.config.customEvents[ev].color !== "undefined" && this.config.customEvents[ev].color !== "") { // Respect parameter ColoredSymbolOnly also for custom events if (this.config.coloredText) { @@ -348,7 +361,6 @@ Module.register("calendar", { } } - const transformedTitle = CalendarUtils.titleTransform(event.title, this.config.titleReplace); titleWrapper.innerHTML = CalendarUtils.shorten(transformedTitle, this.config.maxTitleLength, this.config.wrapEvents, this.config.maxTitleLines) + repeatingCountTitle; const titleClass = this.titleClassForUrl(event.url); diff --git a/modules/default/calendar/calendarutils.js b/modules/default/calendar/calendarutils.js index e953b635..1bfdf159 100644 --- a/modules/default/calendar/calendarutils.js +++ b/modules/default/calendar/calendarutils.js @@ -90,23 +90,39 @@ const CalendarUtils = { /** * Transforms the title of an event for usage. * Replaces parts of the text as defined in config.titleReplace. - * Shortens title based on config.maxTitleLength and config.wrapEvents * @param {string} title The title to transform. - * @param {object} titleReplace Pairs of strings to be replaced in the title + * @param {object} titleReplace object definition of parts to be replaced in the title + * object definition: + * search: {string,required} RegEx in format //x or simple string to be searched. For (birthday) year calcluation, the element matching the year must be in a RegEx group + * replace: {string,required} Replacement string, may contain match group references (latter is required for year calculation) + * yearmatchgroup: {number,optional} match group for year element * @returns {string} The transformed title. */ titleTransform: function (title, titleReplace) { let transformedTitle = title; - for (let needle in titleReplace) { - const replacement = titleReplace[needle]; + for (let tr in titleReplace) { + let transform = titleReplace[tr]; + if (typeof transform === "object") { + if (typeof transform.search !== "undefined" && transform.search !== "" && typeof transform.replace !== "undefined") { + let regParts = transform.search.match(/^\/(.+)\/([gim]*)$/); + let needle = new RegExp(transform.search, "g"); + if (regParts) { + // the parsed pattern is a regexp with flags. + needle = new RegExp(regParts[1], regParts[2]); + } - const regParts = needle.match(/^\/(.+)\/([gim]*)$/); - if (regParts) { - // the parsed pattern is a regexp. - needle = new RegExp(regParts[1], regParts[2]); + let replacement = transform.replace; + if (typeof transform.yearmatchgroup !== "undefined" && transform.yearmatchgroup !== "") { + const yearmatch = [...title.matchAll(needle)]; + if (yearmatch[0].length >= transform.yearmatchgroup + 1 && yearmatch[0][transform.yearmatchgroup] * 1 >= 1900) { + let calcage = new Date().getFullYear() - yearmatch[0][transform.yearmatchgroup] * 1; + let searchstr = `$${transform.yearmatchgroup}`; + replacement = replacement.replace(searchstr, calcage); + } + } + transformedTitle = transformedTitle.replace(needle, replacement); + } } - - transformedTitle = transformedTitle.replace(needle, replacement); } return transformedTitle; } diff --git a/tests/unit/modules/default/calendar/calendar_utils_spec.js b/tests/unit/modules/default/calendar/calendar_utils_spec.js index 7cf896c1..ff37299f 100644 --- a/tests/unit/modules/default/calendar/calendar_utils_spec.js +++ b/tests/unit/modules/default/calendar/calendar_utils_spec.js @@ -138,11 +138,16 @@ describe("Calendar utils tests", () => { describe("titleTransform and shorten combined", () => { it("should replace the birthday and wrap nicely", () => { - const transformedTitle = CalendarUtils.titleTransform("Michael Teeuw's birthday", { - "De verjaardag van ": "", - "'s birthday": "" - }); + const transformedTitle = CalendarUtils.titleTransform("Michael Teeuw's birthday", [{ search: "'s birthday", replace: "" }]); expect(CalendarUtils.shorten(transformedTitle, 10, true, 2)).toBe("Michael
Teeuw"); }); }); + + describe("titleTransform with yearmatchgroup", () => { + it("should replace the birthday and wrap nicely", () => { + const transformedTitle = CalendarUtils.titleTransform("Luciella '2000", [{ search: "^([^']*) '(\\d{4})$", replace: "$1 ($2.)", yearmatchgroup: 2 }]); + const expectedResult = `Luciella (${new Date().getFullYear() - 2000}.)`; + expect(transformedTitle).toBe(expectedResult); + }); + }); });