Implement Open-Xchange calendar profiles
This commit is contained in:
@@ -42,10 +42,11 @@ import {
|
||||
normalizeHexColor,
|
||||
} from "./calendarViewModel";
|
||||
|
||||
export type CalendarSourceMode = "local" | CalendarSyncSourceKind;
|
||||
export type CalendarSourceMode = "local" | CalendarSyncSourceKind | "open_xchange";
|
||||
type CalendarSourceSwitchMode =
|
||||
| "local"
|
||||
| "caldav"
|
||||
| "open_xchange"
|
||||
| "ics"
|
||||
| "graph"
|
||||
| "ews";
|
||||
@@ -75,6 +76,9 @@ export type CalendarCalDavFormPayload = {
|
||||
sync_interval_seconds: number;
|
||||
sync_direction: CalendarCalDavSyncDirection;
|
||||
conflict_policy: CalendarCalDavConflictPolicy;
|
||||
connector_profile_ref: string;
|
||||
identity_mapping_ref: string;
|
||||
resource_calendar_ref: string;
|
||||
};
|
||||
export type CalendarCollectionFormPayload = {
|
||||
sourceMode: CalendarSourceMode;
|
||||
@@ -115,7 +119,7 @@ export function CalendarCollectionDialog({
|
||||
}: {state: CalendarCollectionDialogState;settings: ApiSettings;source: CalendarSyncSource | null;saving: boolean;syncingSourceId: string;canWrite: boolean;canDelete: boolean;canManageSources: boolean;canSyncSources: boolean;onCancel: () => void;onSave: (payload: CalendarCollectionFormPayload) => Promise<boolean>;onRequestDelete: (calendar: CalendarCollection, eventCount: number | null, loadingEventCount: boolean) => void;onSync: (source: CalendarSyncSource, payload?: {password?: string | null;bearer_token?: string | null;force_full?: boolean;}) => Promise<void>;onDiscover: (payload: CalendarCalDavDiscoveryPayload) => Promise<{calendars: CalendarCalDavDiscoveryCandidate[];}>;}) {
|
||||
const calendar = state.kind === "edit" ? state.calendar : null;
|
||||
const isEdit = Boolean(calendar);
|
||||
const [sourceMode, setSourceMode] = useState<CalendarSourceMode>(source ? source.source_kind : "local");
|
||||
const [sourceMode, setSourceMode] = useState<CalendarSourceMode>(source ? calendarSourceModeForSource(source) : "local");
|
||||
const [name, setName] = useState(calendar?.name ?? "");
|
||||
const [color, setColor] = useState(normalizeHexColor(calendar?.color) || DEFAULT_CALENDAR_COLOR);
|
||||
const [davUrl, setDavUrl] = useState(source?.collection_url ?? "");
|
||||
@@ -132,6 +136,9 @@ export function CalendarCollectionDialog({
|
||||
const [syncIntervalMinutes, setSyncIntervalMinutes] = useState(Math.max(1, Math.round((source?.sync_interval_seconds ?? 900) / 60)));
|
||||
const [syncDirection, setSyncDirection] = useState<CalendarCalDavSyncDirection>(source?.sync_direction ?? "two_way");
|
||||
const [conflictPolicy, setConflictPolicy] = useState<CalendarCalDavConflictPolicy>(source?.conflict_policy ?? "etag");
|
||||
const [connectorProfileRef, setConnectorProfileRef] = useState(metadataValue(source?.metadata, "connector_profile_ref"));
|
||||
const [identityMappingRef, setIdentityMappingRef] = useState(metadataValue(source?.metadata, "identity_mapping_ref"));
|
||||
const [resourceCalendarRef, setResourceCalendarRef] = useState(metadataValue(source?.metadata, "resource_calendar_ref"));
|
||||
const [discoveredCalendars, setDiscoveredCalendars] = useState<CalendarCalDavDiscoveryCandidate[]>([]);
|
||||
const [selectedDiscoveredUrl, setSelectedDiscoveredUrl] = useState(source?.collection_url ?? "");
|
||||
const [discovering, setDiscovering] = useState(false);
|
||||
@@ -173,7 +180,10 @@ export function CalendarCollectionDialog({
|
||||
syncEnabled,
|
||||
syncIntervalMinutes,
|
||||
syncDirection,
|
||||
conflictPolicy
|
||||
conflictPolicy,
|
||||
connectorProfileRef,
|
||||
identityMappingRef,
|
||||
resourceCalendarRef
|
||||
};
|
||||
const initialCollectionDraftKey = useMemo(() => calendarDraftKey(collectionDraft), []);
|
||||
const collectionDirty = calendarDraftKey(collectionDraft) !== initialCollectionDraftKey;
|
||||
@@ -223,7 +233,10 @@ export function CalendarCollectionDialog({
|
||||
sync_enabled: syncEnabled,
|
||||
sync_interval_seconds: syncIntervalMinutes * 60,
|
||||
sync_direction: syncDirection,
|
||||
conflict_policy: conflictPolicy
|
||||
conflict_policy: conflictPolicy,
|
||||
connector_profile_ref: connectorProfileRef,
|
||||
identity_mapping_ref: identityMappingRef,
|
||||
resource_calendar_ref: resourceCalendarRef
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -259,7 +272,8 @@ export function CalendarCollectionDialog({
|
||||
setSyncDirection("inbound");
|
||||
return;
|
||||
}
|
||||
if (next === "caldav") {
|
||||
if (next === "caldav" || next === "open_xchange") {
|
||||
if (next === "open_xchange") setAuthType("basic");
|
||||
setSyncDirection("two_way");
|
||||
}
|
||||
}
|
||||
@@ -355,6 +369,7 @@ export function CalendarCollectionDialog({
|
||||
options={[
|
||||
{ id: "local", label: "i18n:govoplan-calendar.local.dc99d54d" },
|
||||
{ id: "caldav", label: "i18n:govoplan-calendar.caldav.64f9720e", disabled: !canManageSources },
|
||||
{ id: "open_xchange", label: "i18n:govoplan-calendar.open_xchange.637e5b7b", disabled: !canManageSources },
|
||||
{ id: "ics", label: "i18n:govoplan-calendar.ics_webcal.9c55b570", disabled: !canManageSources },
|
||||
{ id: "graph", label: "i18n:govoplan-calendar.graph.9a7405eb", disabled: !canManageSources },
|
||||
{ id: "ews", label: "i18n:govoplan-calendar.exchange.5b13eac7", disabled: !canManageSources }
|
||||
@@ -455,8 +470,24 @@ export function CalendarCollectionDialog({
|
||||
<PasswordField value={bearerToken} onValueChange={setBearerToken} disabled={saving || !canEditSource} autoComplete="new-password" />
|
||||
</label>
|
||||
}
|
||||
<div className={sourceMode === "caldav" ? "calendar-discovery-actions" : "calendar-discovery-actions is-readonly"}>
|
||||
{sourceMode === "caldav" &&
|
||||
{sourceMode === "open_xchange" &&
|
||||
<>
|
||||
<label>
|
||||
<span>i18n:govoplan-calendar.connector_profile_reference.e7697602</span>
|
||||
<input value={connectorProfileRef} onChange={(item) => setConnectorProfileRef(item.target.value)} maxLength={255} disabled={saving || !canEditSource} />
|
||||
</label>
|
||||
<label>
|
||||
<span>i18n:govoplan-calendar.identity_group_mapping_reference.40c3da81</span>
|
||||
<input value={identityMappingRef} onChange={(item) => setIdentityMappingRef(item.target.value)} maxLength={255} disabled={saving || !canEditSource} />
|
||||
</label>
|
||||
<label>
|
||||
<span>i18n:govoplan-calendar.resource_calendar_reference.4df67054</span>
|
||||
<input value={resourceCalendarRef} onChange={(item) => setResourceCalendarRef(item.target.value)} maxLength={255} disabled={saving || !canEditSource} />
|
||||
</label>
|
||||
</>
|
||||
}
|
||||
<div className={calendarSourceUsesCalDav(sourceMode) ? "calendar-discovery-actions" : "calendar-discovery-actions is-readonly"}>
|
||||
{calendarSourceUsesCalDav(sourceMode) &&
|
||||
<Button type="button" onClick={() => void handleDiscover()} disabled={saving || discovering || !canEditSource || !davUrl.trim() || effectiveAuthType === "basic" && !username.trim()}>
|
||||
<RefreshCw size={16} /> {discovering ? "i18n:govoplan-calendar.discovering.1884f689" : "i18n:govoplan-calendar.discover.4827ea22"}
|
||||
</Button>
|
||||
@@ -465,7 +496,7 @@ export function CalendarCollectionDialog({
|
||||
</div>
|
||||
</div>
|
||||
{discoveryError && <p className="calendar-form-error">{discoveryError}</p>}
|
||||
{sourceMode === "caldav" && discoveredCalendars.length > 0 &&
|
||||
{calendarSourceUsesCalDav(sourceMode) && discoveredCalendars.length > 0 &&
|
||||
<label>
|
||||
<span>i18n:govoplan-calendar.calendar.adab5090</span>
|
||||
<select value={selectedDiscoveredUrl} onChange={handleDiscoveredCalendarChange} disabled={saving || !canEditSource}>
|
||||
@@ -493,14 +524,14 @@ export function CalendarCollectionDialog({
|
||||
</label>
|
||||
<label>
|
||||
<span>i18n:govoplan-calendar.direction.fd8e45ba</span>
|
||||
<select value={sourceMode === "caldav" ? syncDirection : "inbound"} onChange={(item) => setSyncDirection(item.target.value as CalendarCalDavSyncDirection)} disabled={saving || !canEditMutableSourceSettings || sourceMode !== "caldav"}>
|
||||
<select value={calendarSourceUsesCalDav(sourceMode) ? syncDirection : "inbound"} onChange={(item) => setSyncDirection(item.target.value as CalendarCalDavSyncDirection)} disabled={saving || !canEditMutableSourceSettings || !calendarSourceUsesCalDav(sourceMode)}>
|
||||
<option value="two_way">i18n:govoplan-calendar.two_way.ee50a3e6</option>
|
||||
<option value="inbound">i18n:govoplan-calendar.inbound_only.bf4269b0</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>i18n:govoplan-calendar.conflict_policy.5810e150</span>
|
||||
<select value={conflictPolicy} onChange={(item) => setConflictPolicy(item.target.value as CalendarCalDavConflictPolicy)} disabled={saving || !canEditMutableSourceSettings || sourceMode !== "caldav"}>
|
||||
<select value={conflictPolicy} onChange={(item) => setConflictPolicy(item.target.value as CalendarCalDavConflictPolicy)} disabled={saving || !canEditMutableSourceSettings || !calendarSourceUsesCalDav(sourceMode)}>
|
||||
<option value="etag">i18n:govoplan-calendar.require_matching_etag.0ab1ffe1</option>
|
||||
<option value="overwrite">i18n:govoplan-calendar.overwrite_remote.39625e32</option>
|
||||
</select>
|
||||
@@ -667,9 +698,9 @@ export function syncSourceCreatePayload(sourceMode: CalendarSourceMode, payload:
|
||||
username: sourceKind !== "graph" && payload.auth_type === "basic" ? payload.username.trim() || null : null,
|
||||
sync_enabled: payload.sync_enabled,
|
||||
sync_interval_seconds: Math.max(60, payload.sync_interval_seconds),
|
||||
sync_direction: sourceKind === "caldav" ? payload.sync_direction : "inbound",
|
||||
sync_direction: calendarSourceUsesCalDav(sourceMode) ? payload.sync_direction : "inbound",
|
||||
conflict_policy: payload.conflict_policy,
|
||||
metadata: {}
|
||||
metadata: calendarSourceMetadata(sourceMode, payload)
|
||||
};
|
||||
if (payload.credential_envelope_id) {
|
||||
result.credential_ref = credentialEnvelopeRef(payload.credential_envelope_id);
|
||||
@@ -681,11 +712,19 @@ export function syncSourceCreatePayload(sourceMode: CalendarSourceMode, payload:
|
||||
return result;
|
||||
}
|
||||
|
||||
export function syncSourceConnectionUpdatePayload(payload: CalendarCalDavFormPayload): CalendarSyncSourceUpdatePayload {
|
||||
export function syncSourceConnectionUpdatePayload(
|
||||
sourceMode: CalendarSourceMode,
|
||||
payload: CalendarCalDavFormPayload,
|
||||
currentMetadata: Record<string, unknown> | null | undefined,
|
||||
): CalendarSyncSourceUpdatePayload {
|
||||
const result: CalendarSyncSourceUpdatePayload = {
|
||||
collection_url: payload.collection_url.trim(),
|
||||
auth_type: payload.auth_type,
|
||||
username: payload.auth_type === "basic" ? payload.username.trim() || null : null
|
||||
username: payload.auth_type === "basic" ? payload.username.trim() || null : null,
|
||||
metadata: {
|
||||
...(currentMetadata ?? {}),
|
||||
...calendarSourceMetadata(sourceMode, payload)
|
||||
}
|
||||
};
|
||||
if (payload.credential_envelope_id) {
|
||||
result.credential_ref = credentialEnvelopeRef(payload.credential_envelope_id);
|
||||
@@ -703,6 +742,7 @@ function credentialEnvelopeRef(credentialId: string): string {
|
||||
|
||||
function syncSourceKindForMode(sourceMode: CalendarSourceMode, collectionUrl: string): CalendarSyncSourceKind {
|
||||
if (sourceMode === "ics" && collectionUrl.trim().toLowerCase().startsWith("webcal://")) return "webcal";
|
||||
if (sourceMode === "open_xchange") return "caldav";
|
||||
return sourceMode === "local" ? "caldav" : sourceMode;
|
||||
}
|
||||
|
||||
@@ -718,6 +758,7 @@ function syncTransientPayload(authType: CalendarCalDavAuthType, password: string
|
||||
|
||||
function calendarSourcePaneTitle(sourceMode: CalendarSourceMode): string {
|
||||
if (sourceMode === "caldav") return "i18n:govoplan-calendar.caldav_source.459ed16a";
|
||||
if (sourceMode === "open_xchange") return "i18n:govoplan-calendar.open_xchange_source.4cc03862";
|
||||
if (sourceMode === "ics" || sourceMode === "webcal") return "i18n:govoplan-calendar.ics_webcal_subscription.03bc0d63";
|
||||
if (sourceMode === "graph") return "i18n:govoplan-calendar.microsoft_graph_source.ec4f1383";
|
||||
if (sourceMode === "ews") return "i18n:govoplan-calendar.exchange_web_services_source.53caabf3";
|
||||
@@ -726,6 +767,7 @@ function calendarSourcePaneTitle(sourceMode: CalendarSourceMode): string {
|
||||
|
||||
function calendarSourceUrlLabel(sourceMode: CalendarSourceMode): string {
|
||||
if (sourceMode === "caldav") return "i18n:govoplan-calendar.dav_url.4205e180";
|
||||
if (sourceMode === "open_xchange") return "i18n:govoplan-calendar.open_xchange_dav_url.9d1adeaf";
|
||||
if (sourceMode === "graph") return "i18n:govoplan-calendar.graph_calendar_url.e59607f3";
|
||||
if (sourceMode === "ews") return "i18n:govoplan-calendar.ews_endpoint.a3273983";
|
||||
return "i18n:govoplan-calendar.subscription_url.b8b6a1a0";
|
||||
@@ -733,11 +775,41 @@ function calendarSourceUrlLabel(sourceMode: CalendarSourceMode): string {
|
||||
|
||||
function calendarSourceUrlPlaceholder(sourceMode: CalendarSourceMode): string {
|
||||
if (sourceMode === "caldav") return "https://cloud.example.org/remote.php/dav";
|
||||
if (sourceMode === "open_xchange") return "https://groupware.example.org/caldav/";
|
||||
if (sourceMode === "graph") return "me/calendar or https://graph.microsoft.com/v1.0/me/calendar/events/delta";
|
||||
if (sourceMode === "ews") return "https://exchange.example.org/EWS/Exchange.asmx";
|
||||
return "webcal://example.org/calendar.ics or https://example.org/calendar.ics";
|
||||
}
|
||||
|
||||
function calendarSourceUsesCalDav(sourceMode: CalendarSourceMode): boolean {
|
||||
return sourceMode === "caldav" || sourceMode === "open_xchange";
|
||||
}
|
||||
|
||||
function calendarSourceModeForSource(source: CalendarSyncSource): CalendarSourceMode {
|
||||
return source.source_kind === "caldav" && metadataValue(source.metadata, "integration_profile") === "open_xchange"
|
||||
? "open_xchange"
|
||||
: source.source_kind;
|
||||
}
|
||||
|
||||
function calendarSourceMetadata(
|
||||
sourceMode: CalendarSourceMode,
|
||||
payload: CalendarCalDavFormPayload,
|
||||
): Record<string, unknown> {
|
||||
if (sourceMode !== "open_xchange") return {};
|
||||
return {
|
||||
integration_profile: "open_xchange",
|
||||
transport_kind: "caldav",
|
||||
connector_profile_ref: payload.connector_profile_ref.trim() || null,
|
||||
identity_mapping_ref: payload.identity_mapping_ref.trim() || null,
|
||||
resource_calendar_ref: payload.resource_calendar_ref.trim() || null
|
||||
};
|
||||
}
|
||||
|
||||
function metadataValue(metadata: Record<string, unknown> | null | undefined, key: string): string {
|
||||
const value = metadata?.[key];
|
||||
return typeof value === "string" ? value.trim() : "";
|
||||
}
|
||||
|
||||
function calendarDeleteActionLabel(calendar: CalendarCollection): string {
|
||||
return calendarIsExternal(calendar) ? "i18n:govoplan-calendar.remove.e963907d" : "i18n:govoplan-calendar.delete.f6fdbe48";
|
||||
}
|
||||
|
||||
@@ -327,7 +327,15 @@ export default function CalendarPage({ settings, auth }: {settings: ApiSettings;
|
||||
color: normalizeHexColor(payload.color) || DEFAULT_CALENDAR_COLOR
|
||||
});
|
||||
if (source && canDeleteCalendars) {
|
||||
const updatedSource = await updateSyncSource(settings, source.id, syncSourceConnectionUpdatePayload(payload.caldav));
|
||||
const updatedSource = await updateSyncSource(
|
||||
settings,
|
||||
source.id,
|
||||
syncSourceConnectionUpdatePayload(
|
||||
payload.sourceMode,
|
||||
payload.caldav,
|
||||
source.metadata,
|
||||
),
|
||||
);
|
||||
setSyncSources((current) => current.map((item) => item.id === source.id ? updatedSource : item));
|
||||
reloadSources = true;
|
||||
}
|
||||
@@ -336,7 +344,9 @@ export default function CalendarPage({ settings, auth }: {settings: ApiSettings;
|
||||
const calendar = await createCalendar(settings, {
|
||||
name,
|
||||
color: normalizeHexColor(payload.color) || DEFAULT_CALENDAR_COLOR,
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC"
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC",
|
||||
owner_type: payload.sourceMode === "open_xchange" && payload.caldav.resource_calendar_ref.trim() ? "resource" : "tenant",
|
||||
owner_id: payload.sourceMode === "open_xchange" ? payload.caldav.resource_calendar_ref.trim() || null : null
|
||||
});
|
||||
let createdSource: CalendarSyncSource | null = null;
|
||||
if (payload.sourceMode !== "local") {
|
||||
|
||||
@@ -38,6 +38,7 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-calendar.confirm_delete.c9f2829e": "Confirm delete",
|
||||
"i18n:govoplan-calendar.confirmed.0542404a": "CONFIRMED",
|
||||
"i18n:govoplan-calendar.conflict_policy.5810e150": "Conflict policy",
|
||||
"i18n:govoplan-calendar.connector_profile_reference.e7697602": "Connector profile reference",
|
||||
"i18n:govoplan-calendar.continuous.04f2ccda": "Continuous",
|
||||
"i18n:govoplan-calendar.credential.8bede3ea": "Credential",
|
||||
"i18n:govoplan-calendar.dav_url.4205e180": "DAV URL",
|
||||
@@ -83,6 +84,7 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-calendar.icalendar.f388476d": "iCalendar",
|
||||
"i18n:govoplan-calendar.ics_webcal_subscription.03bc0d63": "ICS/webcal subscription",
|
||||
"i18n:govoplan-calendar.ics_webcal.9c55b570": "ICS/webcal",
|
||||
"i18n:govoplan-calendar.identity_group_mapping_reference.40c3da81": "Identity/group mapping reference",
|
||||
"i18n:govoplan-calendar.identity.7e5a975b": "Identity",
|
||||
"i18n:govoplan-calendar.inbound_only.bf4269b0": "Inbound only",
|
||||
"i18n:govoplan-calendar.interval.011efcd5": "Interval",
|
||||
@@ -120,6 +122,9 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-calendar.not_scheduled.9c367369": "Not scheduled",
|
||||
"i18n:govoplan-calendar.not_synced.4c205136": "Not synced",
|
||||
"i18n:govoplan-calendar.opaque.3e1d0194": "OPAQUE",
|
||||
"i18n:govoplan-calendar.open_xchange_dav_url.9d1adeaf": "Open-Xchange DAV URL",
|
||||
"i18n:govoplan-calendar.open_xchange_source.4cc03862": "Open-Xchange source",
|
||||
"i18n:govoplan-calendar.open_xchange.637e5b7b": "Open-Xchange",
|
||||
"i18n:govoplan-calendar.organizer_json.3add6f9f": "Organizer JSON",
|
||||
"i18n:govoplan-calendar.organizer.debd1720": "Organizer",
|
||||
"i18n:govoplan-calendar.overwrite_remote.39625e32": "Overwrite remote",
|
||||
@@ -133,6 +138,7 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-calendar.recurrence_id.e0b780ba": "Recurrence ID",
|
||||
"i18n:govoplan-calendar.recurrence.f7ad40f5": "Recurrence",
|
||||
"i18n:govoplan-calendar.refresh.56e3badc": "Refresh",
|
||||
"i18n:govoplan-calendar.resource_calendar_reference.4df67054": "Resource calendar reference",
|
||||
"i18n:govoplan-calendar.related_to_json.2d4e8f59": "Related-To JSON",
|
||||
"i18n:govoplan-calendar.related_to.0e7989ff": "Related-To",
|
||||
"i18n:govoplan-calendar.related.917df91e": "Related",
|
||||
@@ -226,6 +232,7 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-calendar.confirm_delete.c9f2829e": "Confirm delete",
|
||||
"i18n:govoplan-calendar.confirmed.0542404a": "CONFIRMED",
|
||||
"i18n:govoplan-calendar.conflict_policy.5810e150": "Conflict policy",
|
||||
"i18n:govoplan-calendar.connector_profile_reference.e7697602": "Connector-Profilreferenz",
|
||||
"i18n:govoplan-calendar.continuous.04f2ccda": "Continuous",
|
||||
"i18n:govoplan-calendar.credential.8bede3ea": "Credential",
|
||||
"i18n:govoplan-calendar.dav_url.4205e180": "DAV URL",
|
||||
@@ -271,6 +278,7 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-calendar.icalendar.f388476d": "iCalendar",
|
||||
"i18n:govoplan-calendar.ics_webcal_subscription.03bc0d63": "ICS/webcal subscription",
|
||||
"i18n:govoplan-calendar.ics_webcal.9c55b570": "ICS/webcal",
|
||||
"i18n:govoplan-calendar.identity_group_mapping_reference.40c3da81": "Identitäts-/Gruppenzuordnungsreferenz",
|
||||
"i18n:govoplan-calendar.identity.7e5a975b": "Identity",
|
||||
"i18n:govoplan-calendar.inbound_only.bf4269b0": "Inbound only",
|
||||
"i18n:govoplan-calendar.interval.011efcd5": "Interval",
|
||||
@@ -308,6 +316,9 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-calendar.not_scheduled.9c367369": "Not scheduled",
|
||||
"i18n:govoplan-calendar.not_synced.4c205136": "Not synced",
|
||||
"i18n:govoplan-calendar.opaque.3e1d0194": "OPAQUE",
|
||||
"i18n:govoplan-calendar.open_xchange_dav_url.9d1adeaf": "Open-Xchange-DAV-URL",
|
||||
"i18n:govoplan-calendar.open_xchange_source.4cc03862": "Open-Xchange-Quelle",
|
||||
"i18n:govoplan-calendar.open_xchange.637e5b7b": "Open-Xchange",
|
||||
"i18n:govoplan-calendar.organizer_json.3add6f9f": "Organizer JSON",
|
||||
"i18n:govoplan-calendar.organizer.debd1720": "Organizer",
|
||||
"i18n:govoplan-calendar.overwrite_remote.39625e32": "Overwrite remote",
|
||||
@@ -321,6 +332,7 @@ export const generatedTranslations: PlatformTranslations = {
|
||||
"i18n:govoplan-calendar.recurrence_id.e0b780ba": "Recurrence ID",
|
||||
"i18n:govoplan-calendar.recurrence.f7ad40f5": "Wiederholung",
|
||||
"i18n:govoplan-calendar.refresh.56e3badc": "Refresh",
|
||||
"i18n:govoplan-calendar.resource_calendar_reference.4df67054": "Ressourcenkalenderreferenz",
|
||||
"i18n:govoplan-calendar.related_to_json.2d4e8f59": "Related-To JSON",
|
||||
"i18n:govoplan-calendar.related_to.0e7989ff": "Related-To",
|
||||
"i18n:govoplan-calendar.related.917df91e": "Related",
|
||||
|
||||
Reference in New Issue
Block a user