Repository: urlaubsverwaltung/urlaubsverwaltung
Pull request #4808 (2024-07-30 19:09:08)

Bump org.mnode.ical4j:ical4j from 3.2.18 to 4.0.2
Bumps [org.mnode.ical4j:ical4j](https://github.com/ical4j/ical4j) from 3.2.18 to 4.0.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/ical4j/ical4j/releases">org.mnode.ical4j:ical4j's releases</a>.</em></p>
<blockquote>
<h2>ical4j-4.0.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Fixed the return type for the Recur.Builder rscale and skip methods by <a href="https://github.com/lyubomyr-shaydariv"><code>@​lyubomyr-shaydariv</code></a> in <a href="https://redirect.github.com/ical4j/ical4j/pull/625">ical4j/ical4j#625</a></li>
<li>Rebaseline develop branch for 4.0.0 release by <a href="https://github.com/benfortuna"><code>@​benfortuna</code></a> in <a href="https://redirect.github.com/ical4j/ical4j/pull/698">ical4j/ical4j#698</a></li>
<li>Release 4.0.0 by <a href="https://github.com/benfortuna"><code>@​benfortuna</code></a> in <a href="https://redirect.github.com/ical4j/ical4j/pull/699">ical4j/ical4j#699</a></li>
<li>Update README.md by <a href="https://github.com/benfortuna"><code>@​benfortuna</code></a> in <a href="https://redirect.github.com/ical4j/ical4j/pull/700">ical4j/ical4j#700</a></li>
<li>Fix for memory leak and updated java module definition by <a href="https://github.com/benfortuna"><code>@​benfortuna</code></a> in <a href="https://redirect.github.com/ical4j/ical4j/pull/704">ical4j/ical4j#704</a></li>
<li>Use OffsetDateTime to represent FORM 2 (UTC) dates by <a href="https://github.com/benfortuna"><code>@​benfortuna</code></a> in <a href="https://redirect.github.com/ical4j/ical4j/pull/711">ical4j/ical4j#711</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/lyubomyr-shaydariv"><code>@​lyubomyr-shaydariv</code></a> made their first contribution in <a href="https://redirect.github.com/ical4j/ical4j/pull/625">ical4j/ical4j#625</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/ical4j/ical4j/compare/ical4j-3.2.19...ical4j-4.0.2">https://github.com/ical4j/ical4j/compare/ical4j-3.2.19...ical4j-4.0.2</a></p>
<h2>ical4j-4.0.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Update README.md by <a href="https://github.com/benfortuna"><code>@​benfortuna</code></a> in <a href="https://redirect.github.com/ical4j/ical4j/pull/700">ical4j/ical4j#700</a></li>
<li>Fix for memory leak and updated java module definition by <a href="https://github.com/benfortuna"><code>@​benfortuna</code></a> in <a href="https://redirect.github.com/ical4j/ical4j/pull/704">ical4j/ical4j#704</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/ical4j/ical4j/compare/ical4j-4.0.0...ical4j-4.0.1">https://github.com/ical4j/ical4j/compare/ical4j-4.0.0...ical4j-4.0.1</a></p>
<h2>ical4j-4.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Improve sub-component filtering by <a href="https://github.com/benfortuna"><code>@​benfortuna</code></a> in <a href="https://redirect.github.com/ical4j/ical4j/pull/687">ical4j/ical4j#687</a></li>
<li>Final 3.x release before 4.0.0 GA by <a href="https://github.com/benfortuna"><code>@​benfortuna</code></a> in <a href="https://redirect.github.com/ical4j/ical4j/pull/697">ical4j/ical4j#697</a></li>
<li>Rebaseline develop branch for 4.0.0 release by <a href="https://github.com/benfortuna"><code>@​benfortuna</code></a> in <a href="https://redirect.github.com/ical4j/ical4j/pull/698">ical4j/ical4j#698</a></li>
<li>Release 4.0.0 by <a href="https://github.com/benfortuna"><code>@​benfortuna</code></a> in <a href="https://redirect.github.com/ical4j/ical4j/pull/699">ical4j/ical4j#699</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/ical4j/ical4j/compare/ical4j-4.0.0-rc6...ical4j-4.0.0">https://github.com/ical4j/ical4j/compare/ical4j-4.0.0-rc6...ical4j-4.0.0</a></p>
<h2>ical4j-3.2.19</h2>
<h2>What's Changed</h2>
<ul>
<li>Final 3.x release before 4.0.0 GA by <a href="https://github.com/benfortuna"><code>@​benfortuna</code></a> in <a href="https://redirect.github.com/ical4j/ical4j/pull/697">ical4j/ical4j#697</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/ical4j/ical4j/compare/ical4j-3.2.18...ical4j-3.2.19">https://github.com/ical4j/ical4j/compare/ical4j-3.2.18...ical4j-3.2.19</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/ical4j/ical4j/blob/develop/CHANGELOG.md">org.mnode.ical4j:ical4j's changelog</a>.</em></p>
<blockquote>
<h1>==================
iCal4j - Changes</h1>
<p>See the <a href="http://ical4j.github.io/docs/ical4j/release-notes">changelog report</a> for further details.</p>
<p>==</p>
<p>1.0-beta5</p>
<ul>
<li>
<p>Added support for compatibility configuration via properties file</p>
</li>
<li>
<p>Numerous bug fixes</p>
</li>
</ul>
<p>1.0-beta4</p>
<ul>
<li>
<p>Added support for VVENUE</p>
</li>
<li>
<p>Bug fixes</p>
</li>
</ul>
<p>1.0-beta3</p>
<ul>
<li>
<p>Third beta release.</p>
</li>
<li>
<p>Added UidGenerator to help with creation of unique UID properties</p>
</li>
<li>
<p>Added timezone aliases for deprecetated timezone ids</p>
</li>
<li>
<p>Added support for calculating the recurrence set for components, to unify
PeriodRule and VEvent.getConsumedTime() functionality</p>
</li>
<li>
<p>Updated timezone definitions (Olson 2007f)</p>
</li>
<li>
<p>Bug fixes.</p>
</li>
</ul>
<p>1.0-beta2</p>
<ul>
<li>Second beta. General bug fixes. No changes to API. Retains binary
compatibility with previous release.</li>
</ul>
<p>1.0-beta1</p>
<ul>
<li>First iCal4j beta. Expect little API changes until the final release, as
binary compatibility should be maintained from this release onwards.</li>
</ul>
<p>0.9.20</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/ical4j/ical4j/commit/6dc825851152371d252b76fc951f2ba9e1690eb6"><code>6dc8258</code></a> Merge pull request <a href="https://redirect.github.com/ical4j/ical4j/issues/711">#711</a> from ical4j/develop</li>
<li><a href="https://github.com/ical4j/ical4j/commit/0ad88042428d68eef7ebd2fe005217ff5519a7bc"><code>0ad8804</code></a> Support any uid supplier</li>
<li><a href="https://github.com/ical4j/ical4j/commit/abb313170763cf4bf49eaa7f16ddfb1761f8c855"><code>abb3131</code></a> Support any uid supplier</li>
<li><a href="https://github.com/ical4j/ical4j/commit/ffaa94e042b845454c34d174dfc5a29f0ac569f8"><code>ffaa94e</code></a> Support any uid supplier</li>
<li><a href="https://github.com/ical4j/ical4j/commit/952713b28b5365c03e64eb42119a63cce4a55f11"><code>952713b</code></a> Support sequence increment function</li>
<li><a href="https://github.com/ical4j/ical4j/commit/f7f9bcf8b6e3ac114279a2d2518dffa8ccd3d936"><code>f7f9bcf</code></a> Use List.of() to ensure immutability</li>
<li><a href="https://github.com/ical4j/ical4j/commit/5dd450b70abbbc49d7c94ff27514aad29f5ef1fb"><code>5dd450b</code></a> Support any uid supplier</li>
<li><a href="https://github.com/ical4j/ical4j/commit/f101d3001fbade14539154f10b40238bd7ec9422"><code>f101d30</code></a> Implement property accessors</li>
<li><a href="https://github.com/ical4j/ical4j/commit/322427e262d764eb0e2df85e050b3bed2e17de27"><code>322427e</code></a> Updated baseline version</li>
<li><a href="https://github.com/ical4j/ical4j/commit/a8c0d43840fb76b07fbfea4d279809567db71618"><code>a8c0d43</code></a> Added support for predicate-based removal</li>
<li>Additional commits viewable in <a href="https://github.com/ical4j/ical4j/compare/ical4j-3.2.18...ical4j-4.0.2">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.mnode.ical4j:ical4j&package-manager=maven&previous-version=3.2.18&new-version=4.0.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

dependabot[bot]: OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting `@dependabot ignore this major version` or `@dependabot ignore this minor version`. You can also ignore all major, minor, or patch releases for a dependency by adding an [`ignore` condition](https://docs.github.com/en/code-security/supply-chain-security/configuration-options-for-dependency-updates#ignore) with the desired `update_types` to your config file.

If you change your mind, just re-open this PR and I'll resolve any conflicts on it.
derTobsch: @dependabot rebase

Patch:
diff --git a/pom.xml b/pom.xml
index eb1912dea..5ce58c01a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -179,7 +179,7 @@
     <dependency>
       <groupId>org.mnode.ical4j</groupId>
       <artifactId>ical4j</artifactId>
-      <version>3.2.18</version>
+      <version>4.0.2</version>
     </dependency>
 
     <!-- GOOGLE CALENDAR SYNC -->
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/calendar/ICalService.java b/src/main/java/org/synyx/urlaubsverwaltung/calendar/ICalService.java
index cef396e8c..d1daedb24 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/calendar/ICalService.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/calendar/ICalService.java
@@ -2,8 +2,6 @@ package org.synyx.urlaubsverwaltung.calendar;
 
 import net.fortuna.ical4j.data.CalendarOutputter;
 import net.fortuna.ical4j.model.Calendar;
-import net.fortuna.ical4j.model.Date;
-import net.fortuna.ical4j.model.DateTime;
 import net.fortuna.ical4j.model.ParameterList;
 import net.fortuna.ical4j.model.component.VEvent;
 import net.fortuna.ical4j.model.parameter.Cn;
@@ -17,7 +15,6 @@ import net.fortuna.ical4j.model.property.Uid;
 import net.fortuna.ical4j.model.property.XProperty;
 import net.fortuna.ical4j.validate.ValidationException;
 import org.apache.commons.codec.digest.DigestUtils;
-import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.io.ByteArrayResource;
 import org.springframework.stereotype.Service;
@@ -27,20 +24,16 @@ import org.synyx.urlaubsverwaltung.person.Person;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.net.URI;
-import java.text.ParseException;
+import java.time.LocalDate;
 import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
 import java.util.List;
 import java.util.Optional;
 
-import static java.lang.invoke.MethodHandles.lookup;
-import static java.util.Date.from;
 import static net.fortuna.ical4j.model.parameter.Role.REQ_PARTICIPANT;
-import static net.fortuna.ical4j.model.property.CalScale.GREGORIAN;
-import static net.fortuna.ical4j.model.property.Method.CANCEL;
 import static net.fortuna.ical4j.model.property.Transp.VALUE_TRANSPARENT;
-import static net.fortuna.ical4j.model.property.Version.VERSION_2_0;
-import static org.slf4j.LoggerFactory.getLogger;
+import static net.fortuna.ical4j.model.property.immutable.ImmutableCalScale.GREGORIAN;
+import static net.fortuna.ical4j.model.property.immutable.ImmutableMethod.CANCEL;
+import static net.fortuna.ical4j.model.property.immutable.ImmutableVersion.VERSION_2_0;
 import static org.synyx.urlaubsverwaltung.calendar.ICalType.CANCELLED;
 import static org.synyx.urlaubsverwaltung.calendar.ICalType.PUBLISHED;
 
@@ -48,12 +41,8 @@ import static org.synyx.urlaubsverwaltung.calendar.ICalType.PUBLISHED;
 @Service
 public class ICalService {
 
-    private static final Logger LOG = getLogger(lookup().lookupClass());
-
     private final CalendarProperties calendarProperties;
 
-    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
-
     @Autowired
     ICalService(CalendarProperties calendarProperties) {
         this.calendarProperties = calendarProperties;
@@ -71,8 +60,8 @@ public class ICalService {
 
     private Calendar generateCalendar(String title, List<Absence> absences, Person recipient) {
         final Calendar calendar = prepareCalendar(absences, PUBLISHED, recipient);
-        calendar.getProperties().add(new XProperty("X-WR-CALNAME", title));
-        calendar.getProperties().add(new RefreshInterval(new ParameterList(), calendarProperties.getRefreshInterval()));
+        calendar.add(new XProperty("X-WR-CALNAME", title));
+        calendar.add(new RefreshInterval(new ParameterList(), calendarProperties.getRefreshInterval()));
         return calendar;
     }
 
@@ -82,20 +71,20 @@ public class ICalService {
 
     private Calendar prepareCalendar(List<Absence> absences, ICalType method, Person recipient) {
         final Calendar calendar = new Calendar();
-        calendar.getProperties().add(VERSION_2_0);
-        calendar.getProperties().add(new ProdId("-//Urlaubsverwaltung//iCal4j 1.0//DE"));
-        calendar.getProperties().add(GREGORIAN);
-        calendar.getProperties().add(new XProperty("X-MICROSOFT-CALSCALE", GREGORIAN.getValue()));
+        calendar.add(VERSION_2_0);
+        calendar.add(new ProdId("-//Urlaubsverwaltung//iCal4j 1.0//DE"));
+        calendar.add(GREGORIAN);
+        calendar.add(new XProperty("X-MICROSOFT-CALSCALE", GREGORIAN.getValue()));
 
         if (method == CANCELLED) {
-            calendar.getProperties().add(CANCEL);
+            calendar.add(CANCEL);
         }
 
         absences.stream()
             .map(absence -> this.toVEvent(absence, method, absence.getPerson().equals(recipient)))
             .filter(Optional::isPresent)
             .map(Optional::get)
-            .forEach(event -> calendar.getComponents().add(event));
+            .forEach(calendar::add);
 
         return calendar;
     }
@@ -107,57 +96,53 @@ public class ICalService {
 
         final VEvent event;
         if (absence.isAllDay()) {
-            try {
-                final Date startDate = new Date(startDateTime.format(formatter));
-                if (isSameDay(startDateTime, endDateTime)) {
-                    event = new VEvent(startDate, absence.getEventSubject());
-                } else {
-                    final Date endDate = new Date(endDateTime.format(formatter));
-                    event = new VEvent(new Date(startDate), new Date(endDate), absence.getEventSubject());
-                }
-            } catch (ParseException e) {
-                LOG.warn("Could not generate all day ical event for absence {}", absence, e);
-                return Optional.empty();
-            }
-
-            event.getProperties().add(new XProperty("X-MICROSOFT-CDO-ALLDAYEVENT", "TRUE"));
+            event = generateAllDayEvent(absence.getEventSubject(), startDateTime, endDateTime);
         } else {
-            final DateTime start = new DateTime(from(startDateTime.toInstant()));
-            start.setUtc(true);
-            final DateTime end = new DateTime(from(endDateTime.toInstant()));
-            end.setUtc(true);
-
-            event = new VEvent(start, end, absence.getEventSubject());
+            event = new VEvent(startDateTime, endDateTime, absence.getEventSubject());
         }
 
-        event.getProperties().add(new Uid(generateUid(absence)));
+        event.add(new Uid(generateUid(absence)));
         if (absence.getPerson().getEmail() != null) {
-            event.getProperties().add(generateAttendee(absence));
+            event.add(generateAttendee(absence));
         }
 
         if (absence.isHolidayReplacement() || !isOwn) {
-            event.getProperties().add(new Transp(VALUE_TRANSPARENT));
+            event.add(new Transp(VALUE_TRANSPARENT));
         }
 
         if (method == CANCELLED) {
-            event.getProperties().add(new Sequence(1));
+            event.add(new Sequence(1));
         }
 
-        event.getProperties().add(new Organizer(URI.create("mailto:" + calendarProperties.getOrganizer())));
+        event.add(new Organizer(URI.create("mailto:" + calendarProperties.getOrganizer())));
 
         return Optional.of(event);
     }
 
+    private VEvent generateAllDayEvent(String eventSubject, ZonedDateTime startDateTime, ZonedDateTime endDateTime) {
+        final LocalDate startDate = startDateTime.toLocalDate();
+        final LocalDate endDate = endDateTime.toLocalDate();
+
+        final VEvent event;
+        if (isSameDay(startDate, endDate)) {
+            event = new VEvent(startDate, eventSubject);
+        } else {
+            event = new VEvent(startDate, endDate, eventSubject);
+        }
+        event.add(new XProperty("X-MICROSOFT-CDO-ALLDAYEVENT", "TRUE"));
+        return event;
+    }
+
     private Attendee generateAttendee(Absence absence) {
         final Attendee attendee = new Attendee(URI.create("mailto:" + absence.getPerson().getEmail()));
-        attendee.getParameters().add(REQ_PARTICIPANT);
-        attendee.getParameters().add(new Cn(absence.getPerson().getNiceName()));
+        attendee.add(REQ_PARTICIPANT);
+        attendee.add(new Cn(absence.getPerson().getNiceName()));
 
         return attendee;
     }
 
-    private boolean isSameDay(ZonedDateTime startDateTime, ZonedDateTime endDate) {
-        return startDateTime.toLocalDate().isEqual(endDate.toLocalDate().minusDays(1));
+    private boolean isSameDay(LocalDate startDate, LocalDate endDate) {
+        return startDate.isEqual(endDate.minusDays(1));
     }
 
     private String generateUid(Absence absence) {


Test patch:
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/calendar/ICalServiceTest.java b/src/test/java/org/synyx/urlaubsverwaltung/calendar/ICalServiceTest.java
index 99d77d0fd..e6ba4dd91 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/calendar/ICalServiceTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/calendar/ICalServiceTest.java
@@ -107,8 +107,8 @@ class ICalServiceTest {
                 REFRESH-INTERVAL:P1D
                 BEGIN:VEVENT
                 DTSTAMP:<removedByConversionMethod>
-                DTSTART:20190426T080000Z
-                DTEND:20190426T120000Z
+                DTSTART;TZID=Etc/UTC:20190426T080000
+                DTEND;TZID=Etc/UTC:20190426T120000
                 SUMMARY:Marlene Muster abwesend
                 UID:45BCD3F64AD76CEF9040DF93047C41B3
                 ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org
@@ -173,8 +173,8 @@ class ICalServiceTest {
                 REFRESH-INTERVAL:P1D
                 BEGIN:VEVENT
                 DTSTAMP:<removedByConversionMethod>
-                DTSTART:20190526T120000Z
-                DTEND:20190526T160000Z
+                DTSTART;TZID=Etc/UTC:20190526T120000
+                DTEND;TZID=Etc/UTC:20190526T160000
                 SUMMARY:Marlene Muster abwesend
                 UID:BB2267885BD8DF263E88D3062853E8A7
                 ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org
@@ -208,8 +208,8 @@ class ICalServiceTest {
                 REFRESH-INTERVAL:P1D
                 BEGIN:VEVENT
                 DTSTAMP:<removedByConversionMethod>
-                DTSTART:20190526T120000Z
-                DTEND:20190526T160000Z
+                DTSTART;TZID=Etc/UTC:20190526T120000
+                DTEND;TZID=Etc/UTC:20190526T160000
                 SUMMARY:Marlene Muster abwesend
                 UID:BB2267885BD8DF263E88D3062853E8A7
                 ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org
@@ -243,8 +243,8 @@ class ICalServiceTest {
                 METHOD:CANCEL
                 BEGIN:VEVENT
                 DTSTAMP:<removedByConversionMethod>
-                DTSTART:20190526T120000Z
-                DTEND:20190526T160000Z
+                DTSTART;TZID=Etc/UTC:20190526T120000
+                DTEND;TZID=Etc/UTC:20190526T160000
                 SUMMARY:Marlene Muster abwesend
                 UID:BB2267885BD8DF263E88D3062853E8A7
                 ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org
@@ -277,8 +277,8 @@ class ICalServiceTest {
                 X-MICROSOFT-CALSCALE:GREGORIAN
                 BEGIN:VEVENT
                 DTSTAMP:<removedByConversionMethod>
-                DTSTART:20190526T120000Z
-                DTEND:20190526T160000Z
+                DTSTART;TZID=Etc/UTC:20190526T120000
+                DTEND;TZID=Etc/UTC:20190526T160000
                 SUMMARY:Marlene Muster abwesend
                 UID:BB2267885BD8DF263E88D3062853E8A7
                 ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Marlene Muster:mailto:muster@example.org