mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-08 04:20:59 -05:00
groupware: more mock data, added missing JMAP types
This commit is contained in:
+532
-32
@@ -7,11 +7,64 @@ import (
|
|||||||
"github.com/opencloud-eu/opencloud/pkg/jscalendar"
|
"github.com/opencloud-eu/opencloud/pkg/jscalendar"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// https://www.iana.org/assignments/jmap/jmap.xml#jmap-data-types
|
||||||
|
type ObjectType string
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
type UTCDate struct {
|
type UTCDateTime struct {
|
||||||
time.Time
|
time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
type LocalDate struct {
|
||||||
|
time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should the calendar’s events be used as part of availability calculation?
|
||||||
|
//
|
||||||
|
// This MUST be one of:
|
||||||
|
// !- `all“: all events are considered.
|
||||||
|
// !- `attending“: events the user is a confirmed or tentative participant of are considered.
|
||||||
|
// !- `none“: all events are ignored (but may be considered if also in another calendar).
|
||||||
|
//
|
||||||
|
// This should default to “all” for the calendars in the user’s own account, and “none” for calendars shared with the user.
|
||||||
|
type IncludeInAvailability string
|
||||||
|
|
||||||
|
type TypeOfCalendarAlert string
|
||||||
|
|
||||||
|
// `CalendarEventNotification` type.
|
||||||
|
//
|
||||||
|
// This MUST be one of
|
||||||
|
// !- `created`
|
||||||
|
// !- `updated`
|
||||||
|
// !- `destroyed`
|
||||||
|
type CalendarEventNotificationTypeOption string
|
||||||
|
|
||||||
|
// `Principal` type.
|
||||||
|
//
|
||||||
|
// This MUST be one of the following values:
|
||||||
|
// !- `individual`: This represents a single person.
|
||||||
|
// !- `group`: This represents a group of people.
|
||||||
|
// !- `resource`: This represents some resource, e.g. a projector.
|
||||||
|
// !- `location`: This represents a location.
|
||||||
|
// !- `other`: This represents some other undefined principal.
|
||||||
|
type PrincipalTypeOption string
|
||||||
|
|
||||||
|
// Algorithms in this list MUST be present in the ["HTTP Digest Algorithm Values" registry]
|
||||||
|
// defined by [RFC3230]; however, in JMAP, they must be lowercased, e.g., "md5" rather than
|
||||||
|
// "MD5".
|
||||||
|
//
|
||||||
|
// Clients SHOULD prefer algorithms listed earlier in this list.
|
||||||
|
//
|
||||||
|
// ["HTTP Digest Algorithm Values" registry]: https://www.iana.org/assignments/http-dig-alg/http-dig-alg.xhtml
|
||||||
|
type HttpDigestAlgorithm string
|
||||||
|
|
||||||
|
// The ResourceType data type is used to act as a unit of measure for the quota usage.
|
||||||
|
type ResourceType string
|
||||||
|
|
||||||
|
// The Scope data type is used to represent the entities the quota applies to.
|
||||||
|
type Scope string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
JmapCore = "urn:ietf:params:jmap:core"
|
JmapCore = "urn:ietf:params:jmap:core"
|
||||||
JmapMail = "urn:ietf:params:jmap:mail"
|
JmapMail = "urn:ietf:params:jmap:mail"
|
||||||
@@ -24,6 +77,30 @@ const (
|
|||||||
JmapBlob = "urn:ietf:params:jmap:blob"
|
JmapBlob = "urn:ietf:params:jmap:blob"
|
||||||
JmapQuota = "urn:ietf:params:jmap:quota"
|
JmapQuota = "urn:ietf:params:jmap:quota"
|
||||||
JmapWebsocket = "urn:ietf:params:jmap:websocket"
|
JmapWebsocket = "urn:ietf:params:jmap:websocket"
|
||||||
|
JmapPrincipals = "urn:ietf:params:jmap:principals"
|
||||||
|
JmapPrincipalsOwner = "urn:ietf:params:jmap:principals:owner"
|
||||||
|
|
||||||
|
CoreType = ObjectType("Core")
|
||||||
|
PushSubscriptionType = ObjectType("PushSubscription")
|
||||||
|
MailboxType = ObjectType("Mailbox")
|
||||||
|
ThreadType = ObjectType("Thread")
|
||||||
|
EmailType = ObjectType("Email")
|
||||||
|
EmailDeliveryType = ObjectType("EmailDelivery")
|
||||||
|
SearchSnippetType = ObjectType("SearchSnippet")
|
||||||
|
IdentityType = ObjectType("Identity")
|
||||||
|
EmailSubmissionType = ObjectType("EmailSubmission")
|
||||||
|
VacationResponseType = ObjectType("VacationResponse")
|
||||||
|
MDNType = ObjectType("MDN")
|
||||||
|
QuotaType = ObjectType("Quota")
|
||||||
|
SieveScriptType = ObjectType("SieveScript")
|
||||||
|
PrincipalType = ObjectType("PrincipalType")
|
||||||
|
ShareNotificationType = ObjectType("ShareNotification")
|
||||||
|
AddressBookType = ObjectType("AddressBook")
|
||||||
|
ContactCardType = ObjectType("ContactCard")
|
||||||
|
CalendarType = ObjectType("Calendar")
|
||||||
|
CalendarEventType = ObjectType("CalendarEvent")
|
||||||
|
CalendarEventNotificationType = ObjectType("CalendarEventNotification")
|
||||||
|
ParticipantIdentityType = ObjectType("ParticipantIdentity")
|
||||||
|
|
||||||
JmapKeywordPrefix = "$"
|
JmapKeywordPrefix = "$"
|
||||||
JmapKeywordSeen = "$seen"
|
JmapKeywordSeen = "$seen"
|
||||||
@@ -47,9 +124,71 @@ const (
|
|||||||
JmapMailboxRoleSent = "sent"
|
JmapMailboxRoleSent = "sent"
|
||||||
//JmapMailboxRoleSubscribed = "subscribed"
|
//JmapMailboxRoleSubscribed = "subscribed"
|
||||||
JmapMailboxRoleTrash = "trash"
|
JmapMailboxRoleTrash = "trash"
|
||||||
|
|
||||||
|
CalendarAlertType = TypeOfCalendarAlert("CalendarAlert")
|
||||||
|
|
||||||
|
CalendarEventNotificationTypeOptionCreated = CalendarEventNotificationTypeOption("created")
|
||||||
|
CalendarEventNotificationTypeOptionUpdated = CalendarEventNotificationTypeOption("updated")
|
||||||
|
CalendarEventNotificationTypeOptionDestroyed = CalendarEventNotificationTypeOption("destroyed")
|
||||||
|
|
||||||
|
PrincipalTypeOptionIndividual = PrincipalTypeOption("individual")
|
||||||
|
PrincipalTypeOptionGroup = PrincipalTypeOption("group")
|
||||||
|
PrincipalTypeOptionResource = PrincipalTypeOption("resource")
|
||||||
|
PrincipalTypeOptionLocation = PrincipalTypeOption("location")
|
||||||
|
PrincipalTypeOptionOther = PrincipalTypeOption("other")
|
||||||
|
|
||||||
|
HttpDigestAlgorithmAdler32 = HttpDigestAlgorithm("adler32")
|
||||||
|
HttpDigestAlgorithmCrc32c = HttpDigestAlgorithm("crc32c")
|
||||||
|
HttpDigestAlgorithmMd5 = HttpDigestAlgorithm("md5")
|
||||||
|
HttpDigestAlgorithmSha = HttpDigestAlgorithm("sha")
|
||||||
|
HttpDigestAlgorithmSha256 = HttpDigestAlgorithm("sha-256")
|
||||||
|
HttpDigestAlgorithmSha512 = HttpDigestAlgorithm("sha-512")
|
||||||
|
HttpDigestAlgorithmUnixSum = HttpDigestAlgorithm("unixsum")
|
||||||
|
HttpDigestAlgorithmUnixcksum = HttpDigestAlgorithm("unixcksum")
|
||||||
|
|
||||||
|
// The quota is measured in a number of data type objects.
|
||||||
|
//
|
||||||
|
// For example, a quota can have a limit of 50 `Mail` objects.
|
||||||
|
ResourceTypeCount = ResourceType("count")
|
||||||
|
|
||||||
|
// The quota is measured in size (in octets).
|
||||||
|
//
|
||||||
|
// For example, a quota can have a limit of 25000 octets.
|
||||||
|
ResourceTypeOctets = ResourceType("octets")
|
||||||
|
|
||||||
|
// The quota information applies to just the client's account.
|
||||||
|
ScopeAccount = Scope("account")
|
||||||
|
// The quota information applies to all accounts sharing this domain.
|
||||||
|
ScopeDomain = Scope("domain")
|
||||||
|
// The quota information applies to all accounts belonging to the server.
|
||||||
|
ScopeGlobal = Scope("global")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
ObjectTypes = []ObjectType{
|
||||||
|
CoreType,
|
||||||
|
PushSubscriptionType,
|
||||||
|
MailboxType,
|
||||||
|
ThreadType,
|
||||||
|
EmailType,
|
||||||
|
EmailDeliveryType,
|
||||||
|
SearchSnippetType,
|
||||||
|
IdentityType,
|
||||||
|
EmailSubmissionType,
|
||||||
|
VacationResponseType,
|
||||||
|
MDNType,
|
||||||
|
QuotaType,
|
||||||
|
SieveScriptType,
|
||||||
|
PrincipalType,
|
||||||
|
ShareNotificationType,
|
||||||
|
AddressBookType,
|
||||||
|
ContactCardType,
|
||||||
|
CalendarType,
|
||||||
|
CalendarEventType,
|
||||||
|
CalendarEventNotificationType,
|
||||||
|
ParticipantIdentityType,
|
||||||
|
}
|
||||||
|
|
||||||
JmapMailboxRoles = []string{
|
JmapMailboxRoles = []string{
|
||||||
JmapMailboxRoleInbox,
|
JmapMailboxRoleInbox,
|
||||||
JmapMailboxRoleSent,
|
JmapMailboxRoleSent,
|
||||||
@@ -57,17 +196,43 @@ var (
|
|||||||
JmapMailboxRoleJunk,
|
JmapMailboxRoleJunk,
|
||||||
JmapMailboxRoleTrash,
|
JmapMailboxRoleTrash,
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
// Should the calendar’s events be used as part of availability calculation?
|
CalendarEventNotificationOptionTypes = []CalendarEventNotificationTypeOption{
|
||||||
//
|
CalendarEventNotificationTypeOptionCreated,
|
||||||
// This MUST be one of:
|
CalendarEventNotificationTypeOptionUpdated,
|
||||||
// !- `all“: all events are considered.
|
CalendarEventNotificationTypeOptionDestroyed,
|
||||||
// !- `attending“: events the user is a confirmed or tentative participant of are considered.
|
}
|
||||||
// !- `none“: all events are ignored (but may be considered if also in another calendar).
|
|
||||||
//
|
PrincipalTypeOptions = []PrincipalTypeOption{
|
||||||
// This should default to “all” for the calendars in the user’s own account, and “none” for calendars shared with the user.
|
PrincipalTypeOptionIndividual,
|
||||||
type IncludeInAvailability string
|
PrincipalTypeOptionGroup,
|
||||||
|
PrincipalTypeOptionResource,
|
||||||
|
PrincipalTypeOptionLocation,
|
||||||
|
PrincipalTypeOptionOther,
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpDigestAlgorithms = []HttpDigestAlgorithm{
|
||||||
|
HttpDigestAlgorithmAdler32,
|
||||||
|
HttpDigestAlgorithmCrc32c,
|
||||||
|
HttpDigestAlgorithmMd5,
|
||||||
|
HttpDigestAlgorithmSha,
|
||||||
|
HttpDigestAlgorithmSha256,
|
||||||
|
HttpDigestAlgorithmSha512,
|
||||||
|
HttpDigestAlgorithmUnixSum,
|
||||||
|
HttpDigestAlgorithmUnixcksum,
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceTypes = []ResourceType{
|
||||||
|
ResourceTypeCount,
|
||||||
|
ResourceTypeOctets,
|
||||||
|
}
|
||||||
|
|
||||||
|
Scopes = []Scope{
|
||||||
|
ScopeAccount,
|
||||||
|
ScopeDomain,
|
||||||
|
ScopeGlobal,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IncludeInAvailabilityAll = IncludeInAvailability("all")
|
IncludeInAvailabilityAll = IncludeInAvailability("all")
|
||||||
@@ -263,7 +428,7 @@ type SessionBlobAccountCapabilities struct {
|
|||||||
// Clients SHOULD prefer algorithms listed earlier in this list.
|
// Clients SHOULD prefer algorithms listed earlier in this list.
|
||||||
//
|
//
|
||||||
// ["HTTP Digest Algorithm Values" registry]: https://www.iana.org/assignments/http-dig-alg/http-dig-alg.xhtml
|
// ["HTTP Digest Algorithm Values" registry]: https://www.iana.org/assignments/http-dig-alg/http-dig-alg.xhtml
|
||||||
SupportedDigestAlgorithms []string `json:"supportedDigestAlgorithms"`
|
SupportedDigestAlgorithms []HttpDigestAlgorithm `json:"supportedDigestAlgorithms"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionQuotaAccountCapabilities struct {
|
type SessionQuotaAccountCapabilities struct {
|
||||||
@@ -280,6 +445,20 @@ type SessionContactsAccountCapabilities struct {
|
|||||||
MayCreateAddressBook bool `json:"mayCreateAddressBook"`
|
MayCreateAddressBook bool `json:"mayCreateAddressBook"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SessionPrincipalsAccountCapabilities struct {
|
||||||
|
// The id of the principal in this account that corresponds to the user fetching this object, if any.
|
||||||
|
CurrentUserPrincipalId string `json:"currentUserPrincipalId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SessionPrincipalsOwnerAccountCapabilities struct {
|
||||||
|
// The id of an account with the `urn:ietf:params:jmap:principals` capability that contains the
|
||||||
|
// corresponding `Principal` object.
|
||||||
|
AccountIdForPrincipal string `json:"accountIdForPrincipal,omitempty"`
|
||||||
|
|
||||||
|
// The id of the `Principal` that owns this account.
|
||||||
|
PrincipalId string `json:"principalId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type SessionAccountCapabilities struct {
|
type SessionAccountCapabilities struct {
|
||||||
Mail SessionMailAccountCapabilities `json:"urn:ietf:params:jmap:mail"`
|
Mail SessionMailAccountCapabilities `json:"urn:ietf:params:jmap:mail"`
|
||||||
Submission SessionSubmissionAccountCapabilities `json:"urn:ietf:params:jmap:submission"`
|
Submission SessionSubmissionAccountCapabilities `json:"urn:ietf:params:jmap:submission"`
|
||||||
@@ -288,9 +467,11 @@ type SessionAccountCapabilities struct {
|
|||||||
Blob SessionBlobAccountCapabilities `json:"urn:ietf:params:jmap:blob"`
|
Blob SessionBlobAccountCapabilities `json:"urn:ietf:params:jmap:blob"`
|
||||||
Quota SessionQuotaAccountCapabilities `json:"urn:ietf:params:jmap:quota"`
|
Quota SessionQuotaAccountCapabilities `json:"urn:ietf:params:jmap:quota"`
|
||||||
Contacts SessionContactsAccountCapabilities `json:"urn:ietf:params:jmap:contacts"`
|
Contacts SessionContactsAccountCapabilities `json:"urn:ietf:params:jmap:contacts"`
|
||||||
|
Principals *SessionPrincipalsAccountCapabilities `json:"urn:ietf:params:jmap:principals,omitempty"`
|
||||||
|
PrincipalsOwner *SessionPrincipalsOwnerAccountCapabilities `json:"urn:ietf:params:jmap:principals:owner,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionAccount struct {
|
type Account struct {
|
||||||
// A user-friendly string to show when presenting content from this account, e.g., the email address representing the owner of the account.
|
// A user-friendly string to show when presenting content from this account, e.g., the email address representing the owner of the account.
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
// This is true if the account belongs to the authenticated user rather than a group account or a personal account of another user that has been shared with them.
|
// This is true if the account belongs to the authenticated user rather than a group account or a personal account of another user that has been shared with them.
|
||||||
@@ -357,6 +538,9 @@ type SessionWebsocketCapabilities struct {
|
|||||||
type SessionContactsCapabilities struct {
|
type SessionContactsCapabilities struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SessionPrincipalCapabilities struct {
|
||||||
|
}
|
||||||
|
|
||||||
type SessionCapabilities struct {
|
type SessionCapabilities struct {
|
||||||
Core SessionCoreCapabilities `json:"urn:ietf:params:jmap:core"`
|
Core SessionCoreCapabilities `json:"urn:ietf:params:jmap:core"`
|
||||||
Mail SessionMailCapabilities `json:"urn:ietf:params:jmap:mail"`
|
Mail SessionMailCapabilities `json:"urn:ietf:params:jmap:mail"`
|
||||||
@@ -366,7 +550,8 @@ type SessionCapabilities struct {
|
|||||||
Blob SessionBlobCapabilities `json:"urn:ietf:params:jmap:blob"`
|
Blob SessionBlobCapabilities `json:"urn:ietf:params:jmap:blob"`
|
||||||
Quota SessionQuotaCapabilities `json:"urn:ietf:params:jmap:quota"`
|
Quota SessionQuotaCapabilities `json:"urn:ietf:params:jmap:quota"`
|
||||||
Websocket SessionWebsocketCapabilities `json:"urn:ietf:params:jmap:websocket"`
|
Websocket SessionWebsocketCapabilities `json:"urn:ietf:params:jmap:websocket"`
|
||||||
Contacts SessionContactsCapabilities `json:"urn:ietf:params:jmap:contacts"`
|
Contacts *SessionContactsCapabilities `json:"urn:ietf:params:jmap:contacts"`
|
||||||
|
Principals *SessionPrincipalCapabilities `json:"urn:ietf:params:jmap:principals"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionPrimaryAccounts struct {
|
type SessionPrimaryAccounts struct {
|
||||||
@@ -387,7 +572,7 @@ type State string
|
|||||||
type SessionResponse struct {
|
type SessionResponse struct {
|
||||||
Capabilities SessionCapabilities `json:"capabilities"`
|
Capabilities SessionCapabilities `json:"capabilities"`
|
||||||
|
|
||||||
Accounts map[string]SessionAccount `json:"accounts,omitempty"`
|
Accounts map[string]Account `json:"accounts,omitempty"`
|
||||||
|
|
||||||
// A map of capability URIs (as found in accountCapabilities) to the account id that is considered to be the user’s main or default
|
// A map of capability URIs (as found in accountCapabilities) to the account id that is considered to be the user’s main or default
|
||||||
// account for data pertaining to that capability.
|
// account for data pertaining to that capability.
|
||||||
@@ -2010,15 +2195,6 @@ type EmailSubmissionSetResponse struct {
|
|||||||
// TODO(pbleser-oc) add updated and destroyed when they are needed
|
// TODO(pbleser-oc) add updated and destroyed when they are needed
|
||||||
}
|
}
|
||||||
|
|
||||||
type ObjectType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
VacationResponseType ObjectType = "VacationResponse"
|
|
||||||
EmailType ObjectType = "Email"
|
|
||||||
EmailDeliveryType ObjectType = "EmailDelivery"
|
|
||||||
MailboxType ObjectType = "Mailbox"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Command string
|
type Command string
|
||||||
|
|
||||||
type Invocation struct {
|
type Invocation struct {
|
||||||
@@ -3160,23 +3336,347 @@ type CalendarEvent struct {
|
|||||||
// `recurrenceId` within a particular account.
|
// `recurrenceId` within a particular account.
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
|
|
||||||
baseEventId string
|
// This is only defined if the `id` property is a synthetic id, generated by the
|
||||||
|
// server to represent a particular instance of a recurring event (immutable; server-set).
|
||||||
|
//
|
||||||
|
// This property gives the id of the "real" `CalendarEvent` this was generated from.
|
||||||
|
BaseEventId string `json:"baseEventId,omitempty"`
|
||||||
|
|
||||||
calendarIds map[string]bool
|
// The set of Calendar ids this event belongs to.
|
||||||
|
//
|
||||||
|
// An event MUST belong to one or more Calendars at all times (until it is destroyed).
|
||||||
|
//
|
||||||
|
// The set is represented as an object, with each key being a Calendar id.
|
||||||
|
//
|
||||||
|
// The value for each key in the object MUST be `true`.
|
||||||
|
CalendarIds map[string]bool `json:"calendarIds,omitempty"`
|
||||||
|
|
||||||
isDraft bool
|
// If true, this event is to be considered a draft.
|
||||||
|
//
|
||||||
|
// The server will not send any scheduling messages to participants or send push notifications
|
||||||
|
// for alerts.
|
||||||
|
//
|
||||||
|
// This may only be set to `true` upon creation.
|
||||||
|
//
|
||||||
|
// Once set to `false`, the value cannot be updated to `true`.
|
||||||
|
//
|
||||||
|
// This property MUST NOT appear in `recurrenceOverrides`.
|
||||||
|
IsDraft bool `json:"isDraft,omitzero"`
|
||||||
|
|
||||||
isOrigin bool
|
// Is this the authoritative source for this event (i.e., does it control scheduling for
|
||||||
|
// this event; the event has not been added as a result of an invitation from another calendar system)?
|
||||||
|
//
|
||||||
|
// This is true if, and only if:
|
||||||
|
// !- the event’s `replyTo` property is null; or
|
||||||
|
// !- the account will receive messages sent to at least one of the methods specified in the `replyTo` property of the event.
|
||||||
|
IsOrigin bool `json:"isOrigin,omitzero"`
|
||||||
|
|
||||||
utcStart UTCDate
|
// For simple clients that do not implement time zone support.
|
||||||
|
//
|
||||||
|
// Clients should only use this if also asking the server to expand recurrences, as you cannot accurately
|
||||||
|
// expand a recurrence without the original time zone.
|
||||||
|
//
|
||||||
|
// This property is calculated at fetch time by the server.
|
||||||
|
//
|
||||||
|
// Time zones are political and they can and do change at any time.
|
||||||
|
//
|
||||||
|
// Fetching exactly the same property again may return a different results if the time zone data has been updated on the server.
|
||||||
|
//
|
||||||
|
// Time zone data changes are not considered `updates` to the event.
|
||||||
|
//
|
||||||
|
// If set, the server will convert the UTC date to the event's current time zone and store the local time.
|
||||||
|
//
|
||||||
|
// This property is not included in `CalendarEvent/get` responses by default and must be requested explicitly.
|
||||||
|
//
|
||||||
|
// Floating events (events without a time zone) will be interpreted as per the time zone given as a `CalendarEvent/get` argument.
|
||||||
|
//
|
||||||
|
// Note that it is not possible to accurately calculate the expansion of recurrence rules or recurrence overrides with the
|
||||||
|
// `utcStart` property rather than the local start time. Even simple recurrences such as "repeat weekly" may cross a
|
||||||
|
// daylight-savings boundary and end up at a different UTC time. Clients that wish to use "utcStart" are RECOMMENDED to
|
||||||
|
// request the server expand recurrences.
|
||||||
|
UtcStart UTCDateTime `json:"utcStart,omitzero"`
|
||||||
|
|
||||||
utcEnd UTCDate
|
// The server calculates the end time in UTC from the start/timeZone/duration properties of the event.
|
||||||
|
//
|
||||||
// TODO https://jmap.io/spec-calendars.html#calendar-events
|
// This property is not included by default and must be requested explicitly.
|
||||||
|
//
|
||||||
|
// Like `utcStart`, it is calculated at fetch time if requested and may change due to time zone data changes.
|
||||||
|
//
|
||||||
|
// Floating events will be interpreted as per the time zone given as a `CalendarEvent/get` argument.
|
||||||
|
UtcEnd UTCDateTime `json:"utcEnd,omitzero"`
|
||||||
|
|
||||||
jscalendar.Event
|
jscalendar.Event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A ParticipantIdentity stores information about a URI that represents the user within that account in an event’s participants.
|
||||||
|
type ParticipantIdentity struct {
|
||||||
|
// The id of the ParticipantIdentity (immutable; server-set).
|
||||||
|
Id string `json:"id"`
|
||||||
|
|
||||||
|
// The display name of the participant to use when adding this participant to an event, e.g. "Joe Bloggs".
|
||||||
|
//
|
||||||
|
// default:
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
|
||||||
|
// The URI that represents this participant for scheduling.
|
||||||
|
//
|
||||||
|
// This URI MAY also be the URI for one of the sendTo methods.
|
||||||
|
ScheduleId string `json:"scheduleId"`
|
||||||
|
|
||||||
|
// Represents methods by which the participant may receive invitations and updates to an event.
|
||||||
|
//
|
||||||
|
// The keys in the property value are the available methods and MUST only contain ASCII alphanumeric
|
||||||
|
// characters (`A-Za-z0-9`).
|
||||||
|
//
|
||||||
|
// The value is a URI for the method specified in the key.
|
||||||
|
SendTo map[string]string `json:"sendTo,omitempty"`
|
||||||
|
|
||||||
|
// This SHOULD be true for exactly one participant identity in any account, and MUST NOT be true for more
|
||||||
|
// than one participant identity within an account (server-set).
|
||||||
|
//
|
||||||
|
// The default identity should be used by clients whenever they need to choose an identity for the user
|
||||||
|
// within this account, and they do not have any other information on which to make a choice.
|
||||||
|
//
|
||||||
|
// For example, if creating a scheduled event in this account, the default identity may be automatically
|
||||||
|
// added as an owner. (But the client may ignore this if, for example, it has its own feature to allow
|
||||||
|
// users to choose which identity to use based on the invitees.)
|
||||||
|
IsDefault bool `json:"isDefault,omitzero"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CalendarAlert struct {
|
||||||
|
// This MUST be the string `CalendarAlert`.
|
||||||
|
Type TypeOfCalendarAlert `json:"@type,omitempty"`
|
||||||
|
|
||||||
|
// The account id for the calendar in which the alert triggered.
|
||||||
|
AccountId string `json:"accountId"`
|
||||||
|
|
||||||
|
// The CalendarEvent id for the alert that triggered.
|
||||||
|
//
|
||||||
|
// Note, for a recurring event this is the id of the base event, never a synthetic id for a particular instance.
|
||||||
|
CalendarEventId string `json:"calendarEventId"`
|
||||||
|
|
||||||
|
// The uid property of the CalendarEvent for the alert that triggered.
|
||||||
|
Uid string `json:"uid"`
|
||||||
|
|
||||||
|
// The `recurrenceId` for the instance of the event for which this alert is being
|
||||||
|
// triggered, or null if the event is not recurring.
|
||||||
|
RecurrenceId LocalDate `json:"recurrenceId,omitzero"`
|
||||||
|
|
||||||
|
// The id for the alert that triggered.
|
||||||
|
AlertId string `json:"alertId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
// The name of the person who made the change.
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// The email of the person who made the change, or null if no email is available.
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
|
||||||
|
// The id of the `Principal` corresponding to the person who made the change, if any.
|
||||||
|
//
|
||||||
|
// This will be null if the change was due to receving an iTIP message.
|
||||||
|
PrincipalId string `json:"principalId,omitempty"`
|
||||||
|
|
||||||
|
// The `scheduleId` URI of the person who made the change, if any.
|
||||||
|
//
|
||||||
|
// This will normally be set if the change was made due to receving an iTIP message.
|
||||||
|
ScheduleId string `json:"scheduleId,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CalendarEventNotification struct {
|
||||||
|
// The id of the `CalendarEventNotification`.
|
||||||
|
Id string `json:"id"`
|
||||||
|
|
||||||
|
// The time this notification was created.
|
||||||
|
Created UTCDateTime `json:"created,omitzero"`
|
||||||
|
|
||||||
|
// Who made the change.
|
||||||
|
ChangedBy *Person `json:"person,omitempty"`
|
||||||
|
|
||||||
|
// Comment sent along with the change by the user that made it.
|
||||||
|
//
|
||||||
|
// (e.g. `COMMENT` property in an iTIP message), if any.
|
||||||
|
Comment string `json:"comment,omitempty"`
|
||||||
|
|
||||||
|
// `CalendarEventNotification` type.
|
||||||
|
//
|
||||||
|
// This MUST be one of
|
||||||
|
// !- `created`
|
||||||
|
// !- `updated`
|
||||||
|
// !- `destroyed`
|
||||||
|
Type CalendarEventNotificationTypeOption `json:"type"`
|
||||||
|
|
||||||
|
// The id of the CalendarEvent that this notification is about.
|
||||||
|
//
|
||||||
|
// If the change only affects a single instance of a recurring event, the server MAY set the
|
||||||
|
// `event` and `event`atch properties for just that instance; the `calendarEventId` MUST
|
||||||
|
// still be for the base event.
|
||||||
|
CalendarEventId string `json:"calendarEventId"`
|
||||||
|
|
||||||
|
// Is this event a draft? (created/updated only)
|
||||||
|
IsDraft bool `json:"isDraft,omitzero"`
|
||||||
|
|
||||||
|
// The data before the change (if updated or destroyed),
|
||||||
|
// or the data after creation (if created).
|
||||||
|
Event *jscalendar.Event `json:"event,omitempty"`
|
||||||
|
|
||||||
|
// A patch encoding the change between the data in the event property,
|
||||||
|
// and the data after the update (updated only).
|
||||||
|
EventPatch PatchObject `json:"eventPatch,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Principal represents an individual, group, location (e.g. a room), resource (e.g. a projector) or other entity
|
||||||
|
// in a collaborative environment.
|
||||||
|
//
|
||||||
|
// Sharing in JMAP is generally configured by assigning rights to certain data within an account to other principals,
|
||||||
|
// for example a user may assign permission to read their calendar to a principal representing another user, or their team.
|
||||||
|
//
|
||||||
|
// In a shared environment such as a workplace, a user may have access to a large number of principals.
|
||||||
|
//
|
||||||
|
// In most systems the user will have access to a single `Account` containing `Principal` objects, but they may
|
||||||
|
// have access to multiple if, for example, aggregating data from different places.
|
||||||
|
type Principal struct {
|
||||||
|
// The id of the principal.
|
||||||
|
Id string `json:"id"`
|
||||||
|
|
||||||
|
// `Principal` type.
|
||||||
|
//
|
||||||
|
// This MUST be one of the following values:
|
||||||
|
// !- `individual`: This represents a single person.
|
||||||
|
// !- `group`: This represents a group of people.
|
||||||
|
// !- `resource`: This represents some resource, e.g. a projector.
|
||||||
|
// !- `location`: This represents a location.
|
||||||
|
// !- `other`: This represents some other undefined principal.
|
||||||
|
Type PrincipalTypeOption `json:"type"`
|
||||||
|
|
||||||
|
// The name of the principal, e.g. `"Jane Doe"`, or `"Room 4B"`.
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// A longer description of the principal, for example details about the
|
||||||
|
// facilities of a resource, or null if no description available.
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
|
||||||
|
// An email address for the principal, or null if no email is available.
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
|
||||||
|
// The time zone for this principal, if known.
|
||||||
|
//
|
||||||
|
// If not null, the value MUST be a time zone id from the IANA Time Zone Database TZDB.
|
||||||
|
TimeZone string `json:"timeZone,omitempty"`
|
||||||
|
|
||||||
|
// A map of JMAP capability URIs to domain specific information about the principal in relation
|
||||||
|
// to that capability, as defined in the document that registered the capability.
|
||||||
|
Capabilities map[string]any `json:"capabilities,omitempty"`
|
||||||
|
|
||||||
|
// A map of account id to `Account` object for each JMAP Account containing data for
|
||||||
|
// this principal that the user has access to, or null if none.
|
||||||
|
Accounts map[string]Account `json:"accounts,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO https://jmap.io/spec-sharing.html#object-properties
|
||||||
|
type ShareNotification struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type Shareable struct {
|
||||||
|
// Has the user indicated they wish to see this data?
|
||||||
|
//
|
||||||
|
// The initial value for this when data is shared by another user is implementation dependent,
|
||||||
|
// although data types may give advice on appropriate defaults.
|
||||||
|
IsSubscribed bool `json:"isSubscribed,omitzero"`
|
||||||
|
|
||||||
|
// The set of permissions the user currently has.
|
||||||
|
//
|
||||||
|
// Appropriate permissions are domain specific and must be defined per data type.
|
||||||
|
MyRights map[string]bool `json:"myRights,omitempty"`
|
||||||
|
|
||||||
|
// A map of principal id to rights to give that principal, or null if not shared with anyone.
|
||||||
|
//
|
||||||
|
// The account id for the principal id can be found in the capabilities of the `Account` this object is in.
|
||||||
|
//
|
||||||
|
// Users with appropriate permission may set this property to modify who the data is shared with.
|
||||||
|
//
|
||||||
|
// The principal that owns the account this data is in MUST NOT be in the set of sharees; their rights are implicit.
|
||||||
|
ShareWith map[string]map[string]bool `json:"shareWith,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Quota is an object that displays the limit set to an account usage.
|
||||||
|
//
|
||||||
|
// It then shows as well the current usage in regard to that limit.
|
||||||
|
type Quota struct {
|
||||||
|
// The unique identifier for this object.
|
||||||
|
Id string `json:"id"`
|
||||||
|
|
||||||
|
// The resource type of the quota.
|
||||||
|
ResourceType ResourceType `json:"resourceType"`
|
||||||
|
|
||||||
|
// The current usage of the defined quota, using the `resourceType` defined as unit of measure.
|
||||||
|
//
|
||||||
|
// Computation of this value is handled by the server.
|
||||||
|
Used uint `json:"used"`
|
||||||
|
|
||||||
|
// The hard limit set by this quota, using the `resourceType` defined as unit of measure.
|
||||||
|
//
|
||||||
|
// Objects in scope may not be created or updated if this limit is reached.
|
||||||
|
HardLimit uint `json:"hardLimit"`
|
||||||
|
|
||||||
|
// The Scope data type is used to represent the entities the quota applies to.
|
||||||
|
//
|
||||||
|
// It is defined as a "String" with values from the following set:
|
||||||
|
// !- `account`: The quota information applies to just the client's account.
|
||||||
|
// !- `domain`: The quota information applies to all accounts sharing this domain.
|
||||||
|
// !- `global`: The quota information applies to all accounts belonging to the server.
|
||||||
|
Scope Scope `json:"scope"`
|
||||||
|
|
||||||
|
// The name of the quota.
|
||||||
|
//
|
||||||
|
// Useful for managing quotas and using queries for searching.
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// A list of all the type names as defined in the "JMAP Types Names" registry
|
||||||
|
// (e.g., `Email`, `Calendar`, etc.) to which this quota applies.
|
||||||
|
//
|
||||||
|
// This allows the quotas to be assigned to distinct or shared data types.
|
||||||
|
//
|
||||||
|
// The server MUST filter out any types for which the client did not request the associated capability
|
||||||
|
// in the `using` section of the request.
|
||||||
|
//
|
||||||
|
// Further, the server MUST NOT return Quota objects for which there are no types recognized by the client.
|
||||||
|
Types []ObjectType `json:"types,omitempty"`
|
||||||
|
|
||||||
|
// The warn limit set by this quota, using the `resourceType` defined as unit of measure.
|
||||||
|
//
|
||||||
|
// It can be used to send a warning to an entity about to reach the hard limit soon, but with no
|
||||||
|
// action taken yet.
|
||||||
|
//
|
||||||
|
// If set, it SHOULD be lower than the `softLimit` (if present and different from null) and the `hardLimit`.
|
||||||
|
WarnLimit uint `json:"warnLimit,omitzero"`
|
||||||
|
|
||||||
|
// The soft limit set by this quota, using the `resourceType` defined as unit of measure.
|
||||||
|
//
|
||||||
|
// It can be used to still allow some operations but refuse some others.
|
||||||
|
//
|
||||||
|
// What is allowed or not is up to the server.
|
||||||
|
//
|
||||||
|
// For example, it could be used for blocking outgoing events of an entity (sending emails, creating
|
||||||
|
// calendar events, etc.) while still receiving incoming events (receiving emails, receiving calendars
|
||||||
|
// events, etc.).
|
||||||
|
//
|
||||||
|
// If set, it SHOULD be higher than the `warnLimit` (if present and different from null) but lower
|
||||||
|
// than the `hardLimit`.
|
||||||
|
SoftLimit uint `json:"softLimit,omitzero"`
|
||||||
|
|
||||||
|
// Arbitrary, free, human-readable description of this quota.
|
||||||
|
//
|
||||||
|
// It might be used to explain where the different limits come from and explain the entities and data
|
||||||
|
// types this quota applies to.
|
||||||
|
//
|
||||||
|
// The description MUST be encoded in UTF-8 [RFC3629] as described in [RFC8620], Section 1.5, and
|
||||||
|
// selected based on an `Accept-Language` header in the request (as defined in [RFC9110], Section 12.5.4)
|
||||||
|
// or out-of-band information about the user's language or locale.
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type ErrorResponse struct {
|
type ErrorResponse struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ type SignedDuration string // TODO
|
|||||||
type Relationship string
|
type Relationship string
|
||||||
type Display string
|
type Display string
|
||||||
type Rel string
|
type Rel string
|
||||||
type Method string
|
|
||||||
type LocationTypeOption string
|
type LocationTypeOption string
|
||||||
type LocationRelation string
|
type LocationRelation string
|
||||||
type VirtualLocationFeature string
|
type VirtualLocationFeature string
|
||||||
@@ -225,17 +224,6 @@ const (
|
|||||||
RelWorkingCopy = Rel("working-copy")
|
RelWorkingCopy = Rel("working-copy")
|
||||||
RelWorkingCopyOf = Rel("working-copy-of")
|
RelWorkingCopyOf = Rel("working-copy-of")
|
||||||
|
|
||||||
MethodPublish = Method("publish")
|
|
||||||
MethodRequest = Method("request")
|
|
||||||
MethodReply = Method("reply")
|
|
||||||
MethodAdd = Method("add")
|
|
||||||
MethodCancel = Method("cancel")
|
|
||||||
MethodRefresh = Method("refresh")
|
|
||||||
MethodCounter = Method("counter")
|
|
||||||
MethodDeclineCounter = Method("declinecounter")
|
|
||||||
|
|
||||||
// mlr --csv --headerless-csv-output cut -f Token ./location-type-registry-1.csv |sort|perl -ne 'chomp; print "LocationTypeOption".ucfirst($_)." = LocationTypeOption(\"".$_."\")\n"'
|
|
||||||
|
|
||||||
LocationTypeOptionAircraft = LocationTypeOption("aircraft")
|
LocationTypeOptionAircraft = LocationTypeOption("aircraft")
|
||||||
LocationTypeOptionAirport = LocationTypeOption("airport")
|
LocationTypeOptionAirport = LocationTypeOption("airport")
|
||||||
LocationTypeOptionArena = LocationTypeOption("arena")
|
LocationTypeOptionArena = LocationTypeOption("arena")
|
||||||
@@ -537,17 +525,6 @@ var (
|
|||||||
RelWorkingCopyOf,
|
RelWorkingCopyOf,
|
||||||
}
|
}
|
||||||
|
|
||||||
Methods = []Method{
|
|
||||||
MethodPublish,
|
|
||||||
MethodRequest,
|
|
||||||
MethodReply,
|
|
||||||
MethodAdd,
|
|
||||||
MethodCancel,
|
|
||||||
MethodRefresh,
|
|
||||||
MethodCounter,
|
|
||||||
MethodDeclineCounter,
|
|
||||||
}
|
|
||||||
|
|
||||||
LocationTypeOptions = []LocationTypeOption{
|
LocationTypeOptions = []LocationTypeOption{
|
||||||
LocationTypeOptionAircraft,
|
LocationTypeOptionAircraft,
|
||||||
LocationTypeOptionAirport,
|
LocationTypeOptionAirport,
|
||||||
@@ -1781,10 +1758,11 @@ type Object struct {
|
|||||||
// to know which version of the object a scheduling message relates to.
|
// to know which version of the object a scheduling message relates to.
|
||||||
Sequence uint `json:"sequence,omitzero"`
|
Sequence uint `json:"sequence,omitzero"`
|
||||||
|
|
||||||
// This is the iTIP [RFC5546] method, in lowercase.
|
/*
|
||||||
//
|
// CalendarEvent objects MUST NOT have a “method” property as this is only used when representing iTIP
|
||||||
// This MUST only be present if the JSCalendar object represents an iTIP scheduling message.
|
// [@!RFC5546] scheduling messages, not events in a data store.
|
||||||
Method Method `json:"method,omitempty"`
|
Method Method `json:"method,omitempty"`
|
||||||
|
*/
|
||||||
|
|
||||||
// This indicates that the time is not important to display to the user when rendering this calendar object.
|
// This indicates that the time is not important to display to the user when rendering this calendar object.
|
||||||
//
|
//
|
||||||
@@ -2232,3 +2210,5 @@ func (g *Group) UnmarshalJSON(b []byte) error {
|
|||||||
type tmp Group
|
type tmp Group
|
||||||
return json.Unmarshal(b, (*tmp)(g))
|
return json.Unmarshal(b, (*tmp)(g))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mlr --csv --headerless-csv-output cut -f Token ./location-type-registry-1.csv |sort|perl -ne 'chomp; print "LocationTypeOption".ucfirst($_)." = LocationTypeOption(\"".$_."\")\n"'
|
||||||
|
|||||||
@@ -727,7 +727,6 @@ func TestEvent(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Sequence: 3,
|
Sequence: 3,
|
||||||
Method: MethodRefresh,
|
|
||||||
ShowWithoutTime: true,
|
ShowWithoutTime: true,
|
||||||
Locations: map[string]Location{
|
Locations: map[string]Location{
|
||||||
"loc1": {
|
"loc1": {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ examples:
|
|||||||
emailReceivedAt: '2025-09-23T10:58:03Z'
|
emailReceivedAt: '2025-09-23T10:58:03Z'
|
||||||
emailSentAt: '2025-09-23T12:58:03+02:00'
|
emailSentAt: '2025-09-23T12:58:03+02:00'
|
||||||
blobId: 'cfz7vkmhcfwl1gfln02hga2fb3xwsqirirousda0rs1soeosla2p1aiaahcqjwaf'
|
blobId: 'cfz7vkmhcfwl1gfln02hga2fb3xwsqirirousda0rs1soeosla2p1aiaahcqjwaf'
|
||||||
attachmentName: 'Alloy_Yellow_Scale.pdf'
|
|
||||||
attachmentType: 'application/pdf'
|
attachmentType: 'application/pdf'
|
||||||
attachmentSize: 192128
|
attachmentSize: 192128
|
||||||
attachmentDisposition: 'attachment'
|
attachmentDisposition: 'attachment'
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
type SwaggerGetAccountResponse struct {
|
type SwaggerGetAccountResponse struct {
|
||||||
// in: body
|
// in: body
|
||||||
Body struct {
|
Body struct {
|
||||||
*jmap.SessionAccount
|
*jmap.Account
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ func (g *Groupware) GetAccount(w http.ResponseWriter, r *http.Request) {
|
|||||||
// swagger:response GetAccountsResponse200
|
// swagger:response GetAccountsResponse200
|
||||||
type SwaggerGetAccountsResponse struct {
|
type SwaggerGetAccountsResponse struct {
|
||||||
// in: body
|
// in: body
|
||||||
Body map[string]jmap.SessionAccount
|
Body map[string]jmap.Account
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:route GET /groupware/accounts account accounts
|
// swagger:route GET /groupware/accounts account accounts
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ var C1 = jmap.Calendar{
|
|||||||
Type: jscalendar.AlertType,
|
Type: jscalendar.AlertType,
|
||||||
Trigger: jscalendar.AbsoluteTrigger{
|
Trigger: jscalendar.AbsoluteTrigger{
|
||||||
Type: jscalendar.AbsoluteTriggerType,
|
Type: jscalendar.AbsoluteTriggerType,
|
||||||
When: MustParse("2025-09-30T20:34:12Z"),
|
When: mustParseTime("2025-09-30T20:34:12Z"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -63,17 +63,25 @@ var AllCalendars = []jmap.Calendar{C1}
|
|||||||
|
|
||||||
var E1 = jmap.CalendarEvent{
|
var E1 = jmap.CalendarEvent{
|
||||||
Id: "ovei9oqu",
|
Id: "ovei9oqu",
|
||||||
|
CalendarIds: map[string]bool{
|
||||||
|
C1.Id: true,
|
||||||
|
},
|
||||||
|
BaseEventId: "ahtah9qu",
|
||||||
|
IsDraft: true,
|
||||||
|
IsOrigin: true,
|
||||||
|
UtcStart: jmap.UTCDateTime{Time: mustParseTime("2025-10-01T00:00:00Z")},
|
||||||
|
UtcEnd: jmap.UTCDateTime{Time: mustParseTime("2025-10-07T00:00:00Z")},
|
||||||
Event: jscalendar.Event{
|
Event: jscalendar.Event{
|
||||||
Type: jscalendar.EventType,
|
Type: jscalendar.EventType,
|
||||||
Start: jscalendar.LocalDateTime{Time: MustParse("2025-09-30T12:00:00Z")},
|
Start: jscalendar.LocalDateTime{Time: mustParseTime("2025-09-30T12:00:00Z")},
|
||||||
Duration: "PT30M",
|
Duration: "PT30M",
|
||||||
Status: jscalendar.StatusConfirmed,
|
Status: jscalendar.StatusConfirmed,
|
||||||
Object: jscalendar.Object{
|
Object: jscalendar.Object{
|
||||||
CommonObject: jscalendar.CommonObject{
|
CommonObject: jscalendar.CommonObject{
|
||||||
Uid: "9a7ab91a-edca-4988-886f-25e00743430d",
|
Uid: "9a7ab91a-edca-4988-886f-25e00743430d",
|
||||||
ProdId: "Mock 0.0",
|
ProdId: "Mock 0.0",
|
||||||
Created: MustParse("2025-09-29T16:17:18Z"),
|
Created: mustParseTime("2025-09-29T16:17:18Z"),
|
||||||
Updated: MustParse("2025-09-29T16:17:18Z"),
|
Updated: mustParseTime("2025-09-29T16:17:18Z"),
|
||||||
Title: "Meeting of the Minds",
|
Title: "Meeting of the Minds",
|
||||||
Description: "Internal meeting about the grand strategy for the future",
|
Description: "Internal meeting about the grand strategy for the future",
|
||||||
DescriptionContentType: "text/plain",
|
DescriptionContentType: "text/plain",
|
||||||
@@ -104,7 +112,6 @@ var E1 = jmap.CalendarEvent{
|
|||||||
},
|
},
|
||||||
RelatedTo: map[string]jscalendar.Relation{},
|
RelatedTo: map[string]jscalendar.Relation{},
|
||||||
Sequence: 0,
|
Sequence: 0,
|
||||||
Method: jscalendar.MethodAdd,
|
|
||||||
ShowWithoutTime: false,
|
ShowWithoutTime: false,
|
||||||
Locations: map[string]jscalendar.Location{
|
Locations: map[string]jscalendar.Location{
|
||||||
"ux1uokie": {
|
"ux1uokie": {
|
||||||
@@ -139,7 +146,111 @@ var E1 = jmap.CalendarEvent{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// TODO more properties, a lot more properties
|
RecurrenceRules: []jscalendar.RecurrenceRule{
|
||||||
|
{
|
||||||
|
Type: jscalendar.RecurrenceRuleType,
|
||||||
|
Frequency: jscalendar.FrequencyWeekly,
|
||||||
|
Interval: 1,
|
||||||
|
Rscale: jscalendar.RscaleIso8601,
|
||||||
|
Skip: jscalendar.SkipOmit,
|
||||||
|
FirstDayOfWeek: jscalendar.DayOfWeekMonday,
|
||||||
|
Count: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
FreeBusyStatus: jscalendar.FreeBusyStatusBusy,
|
||||||
|
Privacy: jscalendar.PrivacyPublic,
|
||||||
|
ReplyTo: map[jscalendar.ReplyMethod]string{
|
||||||
|
jscalendar.ReplyMethodImip: "mailto:organizer@example.com",
|
||||||
|
},
|
||||||
|
SentBy: "organizer@example.com",
|
||||||
|
Participants: map[string]jscalendar.Participant{
|
||||||
|
"eegh7uph": {
|
||||||
|
Type: jscalendar.ParticipantType,
|
||||||
|
Name: "Anderson Dawes",
|
||||||
|
Email: "adawes@opa.org",
|
||||||
|
Description: "Called the meeting",
|
||||||
|
SendTo: map[jscalendar.SendToMethod]string{
|
||||||
|
jscalendar.SendToMethodImip: "mailto:adawes@opa.org",
|
||||||
|
},
|
||||||
|
Kind: jscalendar.ParticipantKindIndividual,
|
||||||
|
Roles: map[jscalendar.Role]bool{
|
||||||
|
jscalendar.RoleAttendee: true,
|
||||||
|
jscalendar.RoleChair: true,
|
||||||
|
jscalendar.RoleOwner: true,
|
||||||
|
},
|
||||||
|
LocationId: "ux1uokie",
|
||||||
|
Language: "en-GB",
|
||||||
|
ParticipationStatus: jscalendar.ParticipationStatusAccepted,
|
||||||
|
ParticipationComment: "I'll be there for sure",
|
||||||
|
ExpectReply: true,
|
||||||
|
ScheduleAgent: jscalendar.ScheduleAgentServer,
|
||||||
|
ScheduleSequence: 1,
|
||||||
|
ScheduleStatus: []string{"1.0"},
|
||||||
|
ScheduleUpdated: mustParseTime("2025-10-01T11:59:12Z"),
|
||||||
|
SentBy: "adawes@opa.org",
|
||||||
|
InvitedBy: "eegh7uph",
|
||||||
|
Links: map[string]jscalendar.Link{
|
||||||
|
"ieni5eiw": {
|
||||||
|
Type: jscalendar.LinkType,
|
||||||
|
Href: "https://static.wikia.nocookie.net/expanse/images/1/1e/OPA_leader.png/revision/latest?cb=20250121103410",
|
||||||
|
ContentType: "image/png",
|
||||||
|
Rel: jscalendar.RelIcon,
|
||||||
|
Size: 192812,
|
||||||
|
Display: jscalendar.DisplayBadge,
|
||||||
|
Title: "Anderson Dawes' photo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ScheduleId: "mailto:adawes@opa.org",
|
||||||
|
},
|
||||||
|
"xeikie9p": {
|
||||||
|
Type: jscalendar.ParticipantType,
|
||||||
|
Name: "Klaes Ashford",
|
||||||
|
Email: "ashford@opa.org",
|
||||||
|
Description: "As the first officer on the Behemoth",
|
||||||
|
SendTo: map[jscalendar.SendToMethod]string{
|
||||||
|
jscalendar.SendToMethodImip: "mailto:ashford@opa.org",
|
||||||
|
jscalendar.SendToMethodOther: "https://behemoth.example.com/ping/@ashford",
|
||||||
|
},
|
||||||
|
Kind: jscalendar.ParticipantKindIndividual,
|
||||||
|
Roles: map[jscalendar.Role]bool{
|
||||||
|
jscalendar.RoleAttendee: true,
|
||||||
|
},
|
||||||
|
LocationId: "em4eal0o",
|
||||||
|
Language: "en-GB",
|
||||||
|
ParticipationStatus: jscalendar.ParticipationStatusNeedsAction,
|
||||||
|
ExpectReply: true,
|
||||||
|
ScheduleAgent: jscalendar.ScheduleAgentServer,
|
||||||
|
ScheduleSequence: 0,
|
||||||
|
SentBy: "adawes@opa.org",
|
||||||
|
InvitedBy: "eegh7uph",
|
||||||
|
Links: map[string]jscalendar.Link{
|
||||||
|
"oifooj6g": {
|
||||||
|
Type: jscalendar.LinkType,
|
||||||
|
Href: "https://static.wikia.nocookie.net/expanse/images/0/02/Klaes_Ashford_-_Expanse_season_4_promotional_2.png/revision/latest?cb=20191206012007",
|
||||||
|
ContentType: "image/png",
|
||||||
|
Rel: jscalendar.RelIcon,
|
||||||
|
Size: 201291,
|
||||||
|
Display: jscalendar.DisplayBadge,
|
||||||
|
Title: "Ashford on Medina Station",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ScheduleId: "mailto:ashford@opa.org",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Alerts: map[string]jscalendar.Alert{
|
||||||
|
"ahqu4xi0": {
|
||||||
|
Type: jscalendar.AlertType,
|
||||||
|
Trigger: jscalendar.OffsetTrigger{
|
||||||
|
Type: jscalendar.OffsetTriggerType,
|
||||||
|
Offset: "PT-5M",
|
||||||
|
RelativeTo: jscalendar.RelativeToStart,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TimeZone: "UTC",
|
||||||
|
MayInviteSelf: true,
|
||||||
|
MayInviteOthers: true,
|
||||||
|
HideAttendees: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/opencloud-eu/opencloud/pkg/jscontact"
|
"github.com/opencloud-eu/opencloud/pkg/jscontact"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MustParse(text string) time.Time {
|
func mustParseTime(text string) time.Time {
|
||||||
t, err := time.Parse(time.RFC3339, text)
|
t, err := time.Parse(time.RFC3339, text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -53,8 +53,8 @@ var CaminaDrummerContact = jscontact.ContactCard{
|
|||||||
A2.Id: true,
|
A2.Id: true,
|
||||||
},
|
},
|
||||||
Version: jscontact.JSContactVersion_1_0,
|
Version: jscontact.JSContactVersion_1_0,
|
||||||
Created: MustParse("2025-09-30T11:00:12Z").UTC(),
|
Created: mustParseTime("2025-09-30T11:00:12Z").UTC(),
|
||||||
Updated: MustParse("2025-09-30T11:00:12Z").UTC(),
|
Updated: mustParseTime("2025-09-30T11:00:12Z").UTC(),
|
||||||
Kind: jscontact.ContactCardKindIndividual,
|
Kind: jscontact.ContactCardKindIndividual,
|
||||||
Language: "en-GB",
|
Language: "en-GB",
|
||||||
ProdId: "Mock 0.0",
|
ProdId: "Mock 0.0",
|
||||||
@@ -330,7 +330,7 @@ var CaminaDrummerContact = jscontact.ContactCard{
|
|||||||
Notes: map[string]jscontact.Note{
|
Notes: map[string]jscontact.Note{
|
||||||
"n1": {
|
"n1": {
|
||||||
Type: jscontact.NoteType,
|
Type: jscontact.NoteType,
|
||||||
Created: MustParse("2025-09-30T11:00:12Z").UTC(),
|
Created: mustParseTime("2025-09-30T11:00:12Z").UTC(),
|
||||||
Author: &jscontact.Author{
|
Author: &jscontact.Author{
|
||||||
Type: jscontact.AuthorType,
|
Type: jscontact.AuthorType,
|
||||||
Name: "expanse.fandom.com",
|
Name: "expanse.fandom.com",
|
||||||
@@ -348,8 +348,8 @@ var AndersonDawesContact = jscontact.ContactCard{
|
|||||||
A1.Id: true,
|
A1.Id: true,
|
||||||
},
|
},
|
||||||
Version: jscontact.JSContactVersion_1_0,
|
Version: jscontact.JSContactVersion_1_0,
|
||||||
Created: MustParse("2025-09-30T11:00:12Z").UTC(),
|
Created: mustParseTime("2025-09-30T11:00:12Z").UTC(),
|
||||||
Updated: MustParse("2025-09-30T11:00:12Z").UTC(),
|
Updated: mustParseTime("2025-09-30T11:00:12Z").UTC(),
|
||||||
Kind: jscontact.ContactCardKindIndividual,
|
Kind: jscontact.ContactCardKindIndividual,
|
||||||
Language: "en-GB",
|
Language: "en-GB",
|
||||||
ProdId: "Mock 0.0",
|
ProdId: "Mock 0.0",
|
||||||
@@ -544,7 +544,7 @@ var AndersonDawesContact = jscontact.ContactCard{
|
|||||||
Kind: jscontact.AnniversaryKindBirth,
|
Kind: jscontact.AnniversaryKindBirth,
|
||||||
Date: jscontact.Timestamp{
|
Date: jscontact.Timestamp{
|
||||||
Type: jscontact.TimestampType,
|
Type: jscontact.TimestampType,
|
||||||
Utc: MustParse("1961-08-24T00:00:00Z"),
|
Utc: mustParseTime("1961-08-24T00:00:00Z"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -105,17 +105,17 @@ func (r Request) GetAccountIdForSubmission() (string, *Error) {
|
|||||||
return r.getAccountId(r.session.PrimaryAccounts.Blob, errNoPrimaryAccountForSubmission)
|
return r.getAccountId(r.session.PrimaryAccounts.Blob, errNoPrimaryAccountForSubmission)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Request) GetAccountForMail() (jmap.SessionAccount, *Error) {
|
func (r Request) GetAccountForMail() (jmap.Account, *Error) {
|
||||||
accountId, err := r.GetAccountIdForMail()
|
accountId, err := r.GetAccountIdForMail()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return jmap.SessionAccount{}, err
|
return jmap.Account{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
account, ok := r.session.Accounts[accountId]
|
account, ok := r.session.Accounts[accountId]
|
||||||
if !ok {
|
if !ok {
|
||||||
r.logger.Debug().Msgf("failed to find account '%v'", accountId)
|
r.logger.Debug().Msgf("failed to find account '%v'", accountId)
|
||||||
// TODO metric for inexistent accounts
|
// TODO metric for inexistent accounts
|
||||||
return jmap.SessionAccount{}, apiError(r.errorId(), ErrorNonExistingAccount,
|
return jmap.Account{}, apiError(r.errorId(), ErrorNonExistingAccount,
|
||||||
withDetail(fmt.Sprintf("The account '%v' does not exist", log.SafeString(accountId))),
|
withDetail(fmt.Sprintf("The account '%v' does not exist", log.SafeString(accountId))),
|
||||||
withSource(&ErrorSource{Parameter: UriParamAccountId}),
|
withSource(&ErrorSource{Parameter: UriParamAccountId}),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user