From 97850daa12ec16521dc8b24dcba0063096ec2a0f Mon Sep 17 00:00:00 2001 From: Rene Krenn Date: Tue, 29 Sep 2026 17:35:46 +0200 Subject: [PATCH 1/5] Issue duty-roster ICS JWTs without an expiry so Google Calendar subscribe URLs stay valid. Co-authored-by: Cursor --- .../java/org/phoenixctms/ctsms/web/util/DefaultSettings.java | 2 +- .../resources/org/phoenixctms/ctsms/web/settings.properties | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/web/src/main/java/org/phoenixctms/ctsms/web/util/DefaultSettings.java b/web/src/main/java/org/phoenixctms/ctsms/web/util/DefaultSettings.java index e9c0a56fb1a5..057358245c9a 100644 --- a/web/src/main/java/org/phoenixctms/ctsms/web/util/DefaultSettings.java +++ b/web/src/main/java/org/phoenixctms/ctsms/web/util/DefaultSettings.java @@ -52,7 +52,7 @@ public final class DefaultSettings { public final static String API_TRUSTED_HOSTS_ONLY_WHITELIST_REGEXP = "/dutyrosterturn/ics"; // "/tools|/selectionset|/search/proband"; public static final int API_DUTYROSTER_ICS_PAST_DAYS = 365; public static final int API_DUTYROSTER_ICS_FUTURE_DAYS = 730; - public static final Long API_DUTYROSTER_ICS_JWT_VALIDITY_SECS = 31536000l; + public static final Long API_DUTYROSTER_ICS_JWT_VALIDITY_SECS = null; public static final boolean DUTY_ROSTER_SCHEDULE_SHOW_GOOGLE_CALENDAR_URL = true; public final static String API_VERSION = "0.0.0"; public static final boolean ENABLE_TOOLTIPS = true; diff --git a/web/src/main/resources/org/phoenixctms/ctsms/web/settings.properties b/web/src/main/resources/org/phoenixctms/ctsms/web/settings.properties index f36112b6f3b9..f010d566cab1 100644 --- a/web/src/main/resources/org/phoenixctms/ctsms/web/settings.properties +++ b/web/src/main/resources/org/phoenixctms/ctsms/web/settings.properties @@ -37,7 +37,8 @@ api_trusted_hosts_only=true api_trusted_hosts_only_whitelist_path_regexp=/dutyrosterturn/ics api_dutyroster_ics_past_days=365 api_dutyroster_ics_future_days=730 -api_dutyroster_ics_jwt_validity_secs=31536000 +# null omits the JWT exp claim, so a Google Calendar subscribe URL stays valid until the password or department key changes. +api_dutyroster_ics_jwt_validity_secs=null duty_roster_schedule_show_google_calendar_url=true From de0aafb1efcb048fd324adbc8cfd8bc6e43cbc86 Mon Sep 17 00:00:00 2001 From: Rene Krenn Date: Tue, 6 Oct 2026 12:12:42 +0200 Subject: [PATCH 2/5] Show the Google Calendar subscribe URL from a configured public base. Co-authored-by: Cursor --- deploy/phoenixctms.org/medunigraz-ics.php | 58 +++++++++++++++++++ .../ctsms/web/util/DefaultSettings.java | 1 + .../ctsms/web/util/SettingCodes.java | 1 + .../phoenixctms/ctsms/web/util/WebUtil.java | 18 ++++-- .../phoenixctms/ctsms/web/settings.properties | 3 + 5 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 deploy/phoenixctms.org/medunigraz-ics.php diff --git a/deploy/phoenixctms.org/medunigraz-ics.php b/deploy/phoenixctms.org/medunigraz-ics.php new file mode 100644 index 000000000000..c0091a125e1a --- /dev/null +++ b/deploy/phoenixctms.org/medunigraz-ics.php @@ -0,0 +1,58 @@ + 8192) { + http_response_code(400); + header('Content-Type: text/plain; charset=UTF-8'); + echo 'Missing jwt'; + exit; +} + +$url = $upstream . '?jwt=' . rawurlencode($jwt); + +if (!function_exists('curl_init')) { + http_response_code(502); + header('Content-Type: text/plain; charset=UTF-8'); + echo 'Upstream request failed'; + exit; +} + +$ch = curl_init($url); +curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); +curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); +curl_setopt($ch, CURLOPT_TIMEOUT, 60); +curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); +curl_setopt($ch, CURLOPT_HEADER, true); +$body = curl_exec($ch); +if ($body === false) { + http_response_code(502); + header('Content-Type: text/plain; charset=UTF-8'); + echo 'Upstream request failed'; + curl_close($ch); + exit; +} +$status = curl_getinfo($ch, CURLINFO_HTTP_CODE); +$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); +curl_close($ch); + +$rawHeaders = substr($body, 0, $headerSize); +$payload = substr($body, $headerSize); +$contentType = 'text/calendar; charset=UTF-8'; +foreach (preg_split("/\r\n|\n|\r/", $rawHeaders) as $headerLine) { + if (stripos($headerLine, 'Content-Type:') === 0) { + $contentType = trim(substr($headerLine, strlen('Content-Type:'))); + break; + } +} + +http_response_code($status > 0 ? $status : 502); +header('Content-Type: ' . $contentType); +echo $payload; diff --git a/web/src/main/java/org/phoenixctms/ctsms/web/util/DefaultSettings.java b/web/src/main/java/org/phoenixctms/ctsms/web/util/DefaultSettings.java index 057358245c9a..304d24a7d000 100644 --- a/web/src/main/java/org/phoenixctms/ctsms/web/util/DefaultSettings.java +++ b/web/src/main/java/org/phoenixctms/ctsms/web/util/DefaultSettings.java @@ -53,6 +53,7 @@ public final class DefaultSettings { public static final int API_DUTYROSTER_ICS_PAST_DAYS = 365; public static final int API_DUTYROSTER_ICS_FUTURE_DAYS = 730; public static final Long API_DUTYROSTER_ICS_JWT_VALIDITY_SECS = null; + public final static String DUTY_ROSTER_GOOGLE_CALENDAR_ICS_BASE_URL = ""; public static final boolean DUTY_ROSTER_SCHEDULE_SHOW_GOOGLE_CALENDAR_URL = true; public final static String API_VERSION = "0.0.0"; public static final boolean ENABLE_TOOLTIPS = true; diff --git a/web/src/main/java/org/phoenixctms/ctsms/web/util/SettingCodes.java b/web/src/main/java/org/phoenixctms/ctsms/web/util/SettingCodes.java index 6bdf0be9bdfe..b0b5b805da79 100644 --- a/web/src/main/java/org/phoenixctms/ctsms/web/util/SettingCodes.java +++ b/web/src/main/java/org/phoenixctms/ctsms/web/util/SettingCodes.java @@ -503,6 +503,7 @@ public interface SettingCodes { public static final String API_DUTYROSTER_ICS_PAST_DAYS = "api_dutyroster_ics_past_days"; public static final String API_DUTYROSTER_ICS_FUTURE_DAYS = "api_dutyroster_ics_future_days"; public static final String API_DUTYROSTER_ICS_JWT_VALIDITY_SECS = "api_dutyroster_ics_jwt_validity_secs"; + public static final String DUTY_ROSTER_GOOGLE_CALENDAR_ICS_BASE_URL = "duty_roster_google_calendar_ics_base_url"; public static final String DUTY_ROSTER_SCHEDULE_SHOW_GOOGLE_CALENDAR_URL = "duty_roster_schedule_show_google_calendar_url"; public static final String API_VERSION = "api_version"; public static final String INPUT_FIELD_DELTA_SUMMARY_MAX = "input_field_delta_summary_max"; diff --git a/web/src/main/java/org/phoenixctms/ctsms/web/util/WebUtil.java b/web/src/main/java/org/phoenixctms/ctsms/web/util/WebUtil.java index 8f1554985ed7..4f089c362b45 100644 --- a/web/src/main/java/org/phoenixctms/ctsms/web/util/WebUtil.java +++ b/web/src/main/java/org/phoenixctms/ctsms/web/util/WebUtil.java @@ -60,7 +60,6 @@ import org.phoenixctms.ctsms.vo.*; import org.phoenixctms.ctsms.web.model.ApplicationScopeBean; import org.phoenixctms.ctsms.web.model.SessionScopeBean; -import org.phoenixctms.ctsms.web.jersey.resource.trial.DutyRosterTurnResource; import org.phoenixctms.ctsms.web.util.Settings.Bundle; import org.primefaces.context.RequestContext; import org.primefaces.model.DefaultStreamedContent; @@ -4316,20 +4315,27 @@ public static StaffOutVO getUserIdentity() { public static boolean isShowDutyRosterGoogleCalendarUrl() { return Settings.getBoolean(SettingCodes.DUTY_ROSTER_SCHEDULE_SHOW_GOOGLE_CALENDAR_URL, Bundle.SETTINGS, DefaultSettings.DUTY_ROSTER_SCHEDULE_SHOW_GOOGLE_CALENDAR_URL) - && getUserIdentity() != null; + && getUserIdentity() != null + && !CommonUtil.isEmptyString(getDutyRosterGoogleCalendarIcsBaseUrl()); } public static String getDutyRosterGoogleCalendarIcsUrl() { String jwt = issueDutyRosterIcsJwt(); - if (CommonUtil.isEmptyString(jwt)) { + String baseUrl = getDutyRosterGoogleCalendarIcsBaseUrl(); + if (CommonUtil.isEmptyString(jwt) || CommonUtil.isEmptyString(baseUrl)) { return ""; } - StringBuilder sb = new StringBuilder(getHttpBaseUrl()); - sb.append("/").append(REST_API_PATH).append(DutyRosterTurnResource.ICS_PATH); - sb.append("?").append(JWT_QUERY_PARAM).append("=").append(urlEncodeUtf8(jwt)); + StringBuilder sb = new StringBuilder(baseUrl); + sb.append(baseUrl.indexOf('?') >= 0 ? '&' : '?'); + sb.append(JWT_QUERY_PARAM).append("=").append(urlEncodeUtf8(jwt)); return sb.toString(); } + private static String getDutyRosterGoogleCalendarIcsBaseUrl() { + String baseUrl = Settings.getString(SettingCodes.DUTY_ROSTER_GOOGLE_CALENDAR_ICS_BASE_URL, Bundle.SETTINGS, DefaultSettings.DUTY_ROSTER_GOOGLE_CALENDAR_ICS_BASE_URL); + return baseUrl == null ? "" : baseUrl.trim(); + } + private static String issueDutyRosterIcsJwt() { AuthenticationVO sessionAuth = getAuthentication(); if (sessionAuth == null) { diff --git a/web/src/main/resources/org/phoenixctms/ctsms/web/settings.properties b/web/src/main/resources/org/phoenixctms/ctsms/web/settings.properties index f010d566cab1..8cd0de15243c 100644 --- a/web/src/main/resources/org/phoenixctms/ctsms/web/settings.properties +++ b/web/src/main/resources/org/phoenixctms/ctsms/web/settings.properties @@ -40,6 +40,9 @@ api_dutyroster_ics_future_days=730 # null omits the JWT exp claim, so a Google Calendar subscribe URL stays valid until the password or department key changes. api_dutyroster_ics_jwt_validity_secs=null +# Public Google Calendar subscribe base URL. Empty hides the links. Example: https://www.phoenixctms.org/medunigraz-ics.php +duty_roster_google_calendar_ics_base_url= + duty_roster_schedule_show_google_calendar_url=true enable_tooltips=true From 533ec7fce477b0082e5c27350e40ad0e9aa3d6eb Mon Sep 17 00:00:00 2001 From: Rene Krenn Date: Tue, 6 Oct 2026 12:29:09 +0200 Subject: [PATCH 3/5] Move the Med Uni Graz ICS proxy script into the site config repo. Co-authored-by: Cursor --- deploy/phoenixctms.org/medunigraz-ics.php | 58 ----------------------- 1 file changed, 58 deletions(-) delete mode 100644 deploy/phoenixctms.org/medunigraz-ics.php diff --git a/deploy/phoenixctms.org/medunigraz-ics.php b/deploy/phoenixctms.org/medunigraz-ics.php deleted file mode 100644 index c0091a125e1a..000000000000 --- a/deploy/phoenixctms.org/medunigraz-ics.php +++ /dev/null @@ -1,58 +0,0 @@ - 8192) { - http_response_code(400); - header('Content-Type: text/plain; charset=UTF-8'); - echo 'Missing jwt'; - exit; -} - -$url = $upstream . '?jwt=' . rawurlencode($jwt); - -if (!function_exists('curl_init')) { - http_response_code(502); - header('Content-Type: text/plain; charset=UTF-8'); - echo 'Upstream request failed'; - exit; -} - -$ch = curl_init($url); -curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); -curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); -curl_setopt($ch, CURLOPT_TIMEOUT, 60); -curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); -curl_setopt($ch, CURLOPT_HEADER, true); -$body = curl_exec($ch); -if ($body === false) { - http_response_code(502); - header('Content-Type: text/plain; charset=UTF-8'); - echo 'Upstream request failed'; - curl_close($ch); - exit; -} -$status = curl_getinfo($ch, CURLINFO_HTTP_CODE); -$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); -curl_close($ch); - -$rawHeaders = substr($body, 0, $headerSize); -$payload = substr($body, $headerSize); -$contentType = 'text/calendar; charset=UTF-8'; -foreach (preg_split("/\r\n|\n|\r/", $rawHeaders) as $headerLine) { - if (stripos($headerLine, 'Content-Type:') === 0) { - $contentType = trim(substr($headerLine, strlen('Content-Type:'))); - break; - } -} - -http_response_code($status > 0 ? $status : 502); -header('Content-Type: ' . $contentType); -echo $payload; From 98e57443db5cc68d79ed15b4f7ba4b48efc8b408 Mon Sep 17 00:00:00 2001 From: Rene Krenn Date: Tue, 6 Oct 2026 12:46:25 +0200 Subject: [PATCH 4/5] Show Google Calendar links only when the public ICS base URL is set. Co-authored-by: Cursor --- .../java/org/phoenixctms/ctsms/web/util/DefaultSettings.java | 1 - .../java/org/phoenixctms/ctsms/web/util/SettingCodes.java | 1 - web/src/main/java/org/phoenixctms/ctsms/web/util/WebUtil.java | 4 +--- .../resources/org/phoenixctms/ctsms/web/settings.properties | 2 -- 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/web/src/main/java/org/phoenixctms/ctsms/web/util/DefaultSettings.java b/web/src/main/java/org/phoenixctms/ctsms/web/util/DefaultSettings.java index 304d24a7d000..ba8ad4d6885a 100644 --- a/web/src/main/java/org/phoenixctms/ctsms/web/util/DefaultSettings.java +++ b/web/src/main/java/org/phoenixctms/ctsms/web/util/DefaultSettings.java @@ -54,7 +54,6 @@ public final class DefaultSettings { public static final int API_DUTYROSTER_ICS_FUTURE_DAYS = 730; public static final Long API_DUTYROSTER_ICS_JWT_VALIDITY_SECS = null; public final static String DUTY_ROSTER_GOOGLE_CALENDAR_ICS_BASE_URL = ""; - public static final boolean DUTY_ROSTER_SCHEDULE_SHOW_GOOGLE_CALENDAR_URL = true; public final static String API_VERSION = "0.0.0"; public static final boolean ENABLE_TOOLTIPS = true; public static final boolean TRIAL_STATUS_UPDATE_REQUIRES_PASSWORD = true; diff --git a/web/src/main/java/org/phoenixctms/ctsms/web/util/SettingCodes.java b/web/src/main/java/org/phoenixctms/ctsms/web/util/SettingCodes.java index b0b5b805da79..94a7dbca6a93 100644 --- a/web/src/main/java/org/phoenixctms/ctsms/web/util/SettingCodes.java +++ b/web/src/main/java/org/phoenixctms/ctsms/web/util/SettingCodes.java @@ -504,7 +504,6 @@ public interface SettingCodes { public static final String API_DUTYROSTER_ICS_FUTURE_DAYS = "api_dutyroster_ics_future_days"; public static final String API_DUTYROSTER_ICS_JWT_VALIDITY_SECS = "api_dutyroster_ics_jwt_validity_secs"; public static final String DUTY_ROSTER_GOOGLE_CALENDAR_ICS_BASE_URL = "duty_roster_google_calendar_ics_base_url"; - public static final String DUTY_ROSTER_SCHEDULE_SHOW_GOOGLE_CALENDAR_URL = "duty_roster_schedule_show_google_calendar_url"; public static final String API_VERSION = "api_version"; public static final String INPUT_FIELD_DELTA_SUMMARY_MAX = "input_field_delta_summary_max"; public static final String FIELD_CALCULATION_DEBUG_LEVEL = "field_calculation_debug_level"; diff --git a/web/src/main/java/org/phoenixctms/ctsms/web/util/WebUtil.java b/web/src/main/java/org/phoenixctms/ctsms/web/util/WebUtil.java index 4f089c362b45..f236a1959458 100644 --- a/web/src/main/java/org/phoenixctms/ctsms/web/util/WebUtil.java +++ b/web/src/main/java/org/phoenixctms/ctsms/web/util/WebUtil.java @@ -4314,9 +4314,7 @@ public static StaffOutVO getUserIdentity() { } public static boolean isShowDutyRosterGoogleCalendarUrl() { - return Settings.getBoolean(SettingCodes.DUTY_ROSTER_SCHEDULE_SHOW_GOOGLE_CALENDAR_URL, Bundle.SETTINGS, DefaultSettings.DUTY_ROSTER_SCHEDULE_SHOW_GOOGLE_CALENDAR_URL) - && getUserIdentity() != null - && !CommonUtil.isEmptyString(getDutyRosterGoogleCalendarIcsBaseUrl()); + return getUserIdentity() != null && !CommonUtil.isEmptyString(getDutyRosterGoogleCalendarIcsBaseUrl()); } public static String getDutyRosterGoogleCalendarIcsUrl() { diff --git a/web/src/main/resources/org/phoenixctms/ctsms/web/settings.properties b/web/src/main/resources/org/phoenixctms/ctsms/web/settings.properties index 8cd0de15243c..9c56ec2e64e1 100644 --- a/web/src/main/resources/org/phoenixctms/ctsms/web/settings.properties +++ b/web/src/main/resources/org/phoenixctms/ctsms/web/settings.properties @@ -43,8 +43,6 @@ api_dutyroster_ics_jwt_validity_secs=null # Public Google Calendar subscribe base URL. Empty hides the links. Example: https://www.phoenixctms.org/medunigraz-ics.php duty_roster_google_calendar_ics_base_url= -duty_roster_schedule_show_google_calendar_url=true - enable_tooltips=true highlight_text_input=false From a62a7b4eae38c302a4e76bff995d35bf417ef47e Mon Sep 17 00:00:00 2001 From: Rene Krenn Date: Thu, 8 Oct 2026 19:32:09 +0200 Subject: [PATCH 5/5] Name the duty-roster calendar from the instance and disable the feed when no public URL is set. Co-authored-by: Cursor --- .../trial/DutyRosterTurnIcsWriter.java | 79 +++++++++++++++++-- .../trial/DutyRosterTurnResource.java | 5 +- .../phoenixctms/ctsms/web/util/WebUtil.java | 6 +- 3 files changed, 82 insertions(+), 8 deletions(-) diff --git a/web/src/main/java/org/phoenixctms/ctsms/web/jersey/resource/trial/DutyRosterTurnIcsWriter.java b/web/src/main/java/org/phoenixctms/ctsms/web/jersey/resource/trial/DutyRosterTurnIcsWriter.java index db0808e89a44..ff945a12eae1 100644 --- a/web/src/main/java/org/phoenixctms/ctsms/web/jersey/resource/trial/DutyRosterTurnIcsWriter.java +++ b/web/src/main/java/org/phoenixctms/ctsms/web/jersey/resource/trial/DutyRosterTurnIcsWriter.java @@ -4,13 +4,21 @@ import java.util.Collection; import java.util.Date; import java.util.Iterator; +import java.util.Locale; import java.util.TimeZone; +import org.phoenixctms.ctsms.domain.Staff; +import org.phoenixctms.ctsms.domain.User; import org.phoenixctms.ctsms.util.CommonUtil; +import org.phoenixctms.ctsms.util.CoreUtil; +import org.phoenixctms.ctsms.vo.AuthenticationVO; import org.phoenixctms.ctsms.vo.DutyRosterTurnOutVO; import org.phoenixctms.ctsms.vo.StaffOutVO; import org.phoenixctms.ctsms.vo.TrialOutVO; import org.phoenixctms.ctsms.vo.VisitScheduleItemOutVO; +import org.phoenixctms.ctsms.web.util.MessageCodes; +import org.phoenixctms.ctsms.web.util.Messages; +import org.phoenixctms.ctsms.web.util.WebUtil; final class DutyRosterTurnIcsWriter { @@ -18,7 +26,15 @@ final class DutyRosterTurnIcsWriter { private static final String TITLE_SEPARATOR = " - "; private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); - static String toIcalendar(Collection dutyRosterTurns, String host) { + static String calendarName(AuthenticationVO auth) { + StringBuilder sb = new StringBuilder(); + appendPart(sb, org.phoenixctms.ctsms.util.Settings.getInstanceName()); + appendPart(sb, dutyRosterLabel()); + appendPart(sb, identityName(auth)); + return sb.toString(); + } + + static String toIcalendar(Collection dutyRosterTurns, String host, String calendarName) { StringBuilder sb = new StringBuilder(); SimpleDateFormat utc = newUtcFormat(); Date now = new Date(); @@ -27,7 +43,7 @@ static String toIcalendar(Collection dutyRosterTurns, Strin sb.append("PRODID:-//Phoenix CTMS//Duty Roster//EN").append(CRLF); sb.append("CALSCALE:GREGORIAN").append(CRLF); sb.append("METHOD:PUBLISH").append(CRLF); - sb.append("X-WR-CALNAME:Phoenix Duty Roster").append(CRLF); + appendTextLine(sb, "X-WR-CALNAME", calendarName); if (dutyRosterTurns != null) { Iterator it = dutyRosterTurns.iterator(); while (it.hasNext()) { @@ -73,16 +89,15 @@ private static String getSummary(DutyRosterTurnOutVO turn) { if (!CommonUtil.isEmptyString(turn.getTitle())) { appendSeparated(sb, turn.getTitle()); } - StaffOutVO staff = turn.getStaff(); - if (staff != null) { - appendSeparated(sb, CommonUtil.staffOutVOToString(staff)); + if (sb.length() == 0) { + return turn.getCalendar(); } return sb.toString(); } private static String getDescription(DutyRosterTurnOutVO turn) { StringBuilder sb = new StringBuilder(); - if (!CommonUtil.isEmptyString(turn.getCalendar())) { + if (hasEventTitle(turn) && !CommonUtil.isEmptyString(turn.getCalendar())) { sb.append(turn.getCalendar()); } if (!CommonUtil.isEmptyString(turn.getComment())) { @@ -94,6 +109,58 @@ private static String getDescription(DutyRosterTurnOutVO turn) { return sb.toString(); } + private static boolean hasEventTitle(DutyRosterTurnOutVO turn) { + if (turn.getTrial() != null) { + return true; + } + VisitScheduleItemOutVO visitScheduleItem = turn.getVisitScheduleItem(); + if (visitScheduleItem != null && !CommonUtil.isEmptyString(visitScheduleItem.getName())) { + return true; + } + return !CommonUtil.isEmptyString(turn.getTitle()); + } + + private static void appendPart(StringBuilder sb, String value) { + if (CommonUtil.isEmptyString(value)) { + return; + } + if (sb.length() > 0) { + sb.append(' '); + } + sb.append(value.trim()); + } + + private static String dutyRosterLabel() { + Locale locale = null; + try { + locale = CoreUtil.getUserContext().getLocale(); + } catch (Exception e) { + } + try { + return CommonUtil.getString(MessageCodes.DUTY_ROSTER_SCHEDULE_MENU_ITEM_LABEL, CommonUtil.getBundle(Messages.MESSAGE_BUNDLE_DEFAULT, locale), "Duty Roster"); + } catch (Exception e) { + return "Duty Roster"; + } + } + + private static String identityName(AuthenticationVO auth) { + if (auth == null) { + return ""; + } + User user = CoreUtil.getUser(); + Staff identity = user == null ? null : user.getIdentity(); + if (identity == null) { + return ""; + } + try { + StaffOutVO staff = WebUtil.getServiceLocator().getStaffService().getStaff(auth, identity.getId(), null, null, null); + String name = CommonUtil.staffOutVOToString(staff); + return name == null ? "" : name; + } catch (Exception e) { + return ""; + } + } + private static void appendSeparated(StringBuilder sb, String value) { if (sb.length() > 0) { sb.append(TITLE_SEPARATOR); diff --git a/web/src/main/java/org/phoenixctms/ctsms/web/jersey/resource/trial/DutyRosterTurnResource.java b/web/src/main/java/org/phoenixctms/ctsms/web/jersey/resource/trial/DutyRosterTurnResource.java index ee3aa44c436d..e356fd329a84 100644 --- a/web/src/main/java/org/phoenixctms/ctsms/web/jersey/resource/trial/DutyRosterTurnResource.java +++ b/web/src/main/java/org/phoenixctms/ctsms/web/jersey/resource/trial/DutyRosterTurnResource.java @@ -68,8 +68,11 @@ public Response getDutyRosterIcs( @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("sort") Boolean sort) throws Exception { + if (!WebUtil.isDutyRosterIcsRailEnabled()) { + return Response.status(Response.Status.NOT_FOUND).build(); + } Collection dutyRosterTurns = loadDutyRosterInterval(departmentId, statusId, staffId, unassigned, trialId, calendar, from, to, sort, true); - String ics = DutyRosterTurnIcsWriter.toIcalendar(dutyRosterTurns, WebUtil.getHttpHost()); + String ics = DutyRosterTurnIcsWriter.toIcalendar(dutyRosterTurns, WebUtil.getHttpHost(), DutyRosterTurnIcsWriter.calendarName(auth)); ResponseBuilder response = Response.ok(ics, TEXT_CALENDAR + ";charset=UTF-8"); response.header("Content-Disposition", "inline; filename=\"" + ICS_FILENAME + "\""); return response.build(); diff --git a/web/src/main/java/org/phoenixctms/ctsms/web/util/WebUtil.java b/web/src/main/java/org/phoenixctms/ctsms/web/util/WebUtil.java index f236a1959458..a58883d8651c 100644 --- a/web/src/main/java/org/phoenixctms/ctsms/web/util/WebUtil.java +++ b/web/src/main/java/org/phoenixctms/ctsms/web/util/WebUtil.java @@ -4313,8 +4313,12 @@ public static StaffOutVO getUserIdentity() { return null; } + public static boolean isDutyRosterIcsRailEnabled() { + return !CommonUtil.isEmptyString(getDutyRosterGoogleCalendarIcsBaseUrl()); + } + public static boolean isShowDutyRosterGoogleCalendarUrl() { - return getUserIdentity() != null && !CommonUtil.isEmptyString(getDutyRosterGoogleCalendarIcsBaseUrl()); + return getUserIdentity() != null && isDutyRosterIcsRailEnabled(); } public static String getDutyRosterGoogleCalendarIcsUrl() {