Commit graph

51 commits

Author SHA1 Message Date
Nick Misasi
20f7cdebcd feat(03-04): update Makefile and generate Go code
- Add hooks_command.proto to Makefile proto mappings
- Update hooks_command.proto to import api_remaining.proto for shared types
  (CommandArgs, CommandResponse, PluginClusterEvent)
- Define new types: WebSocketRequest, SyncMsgJson, SyncResponse, RemoteCluster
- Generated Go code compiles successfully
- Total hooks in PluginHooks service: 41 (excluding deferred ServeHTTP/ServeMetrics)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:42:31 -05:00
Nick Misasi
0c75338a99 feat(03-04): add remaining hook RPCs to PluginHooks service
Update hooks.proto with 11 new RPCs for command/websocket/cluster/shared channels/support hooks:

COMMAND HOOKS:
- ExecuteCommand

WEBSOCKET HOOKS:
- OnWebSocketConnect
- OnWebSocketDisconnect
- WebSocketMessageHasBeenPosted

CLUSTER HOOKS:
- OnPluginClusterEvent

SHARED CHANNELS HOOKS:
- OnSharedChannelsSyncMsg
- OnSharedChannelsPing
- OnSharedChannelsAttachmentSyncMsg
- OnSharedChannelsProfileImageSyncMsg

SUPPORT HOOKS:
- GenerateSupportData

Also added note about ServeHTTP and ServeMetrics being deferred to Phase 8.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:40:25 -05:00
Nick Misasi
8261d03e05 feat(03-04): create hooks_command.proto with command/cluster/websocket hook definitions
Add protobuf message definitions for command, WebSocket, cluster, shared channels,
and support data generation hooks including:
- CommandArgs, CommandResponse, SlackAttachment model types
- PluginClusterEvent, WebSocketRequest model types
- SyncMsgJson, SyncResponse, RemoteCluster for shared channels
- Request/response messages for 11 hooks:
  - ExecuteCommand
  - OnPluginClusterEvent
  - OnWebSocketConnect/Disconnect
  - WebSocketMessageHasBeenPosted
  - OnSharedChannelsSyncMsg/Ping/AttachmentSyncMsg/ProfileImageSyncMsg
  - GenerateSupportData

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:39:45 -05:00
Nick Misasi
8bbc01cca2 feat(03-03): update Makefile and generate Go code for user/channel hooks
- Add hooks_user_channel.proto mapping to Makefile proto generation
- Regenerate Go protobuf code with new user/channel hook types
- Generated hooks_user_channel.pb.go with all message types
- Updated hooks.pb.go and hooks_grpc.pb.go with new RPCs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:37:13 -05:00
Nick Misasi
6f4a0fe8c9 feat(03-03): add user/channel hook RPCs to PluginHooks service
Add 10 RPC definitions to PluginHooks service:
- USER HOOKS: UserHasBeenCreated, UserWillLogIn, UserHasLoggedIn,
  UserHasBeenDeactivated, OnSAMLLogin
- CHANNEL AND TEAM HOOKS: ChannelHasBeenCreated, UserHasJoinedChannel,
  UserHasLeftChannel, UserHasJoinedTeam, UserHasLeftTeam

Import hooks_user_channel.proto for request/response message types.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:36:16 -05:00
Nick Misasi
e6c343c72b feat(03-03): create hooks_user_channel.proto with user/channel hook definitions
Add protobuf definitions for 10 user and channel lifecycle hooks:
- UserHasBeenCreated, UserWillLogIn, UserHasLoggedIn, UserHasBeenDeactivated
- OnSAMLLogin (with JSON blob for SAML assertion)
- ChannelHasBeenCreated
- UserHasJoinedChannel, UserHasLeftChannel (with optional actor)
- UserHasJoinedTeam, UserHasLeftTeam (with optional actor)

Includes SamlAssertionInfoJson type for handling external SAML library types.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:35:44 -05:00
Nick Misasi
5b3ef59f9a feat(03-02): update Makefile and generate Go code for message hooks
- Add hooks_message.proto mapping to Makefile for proto generation
- Update hooks_message.proto to import api_remaining.proto for
  PushNotification and Preference types (avoiding redefinition)
- Generate Go code for all message hook messages and RPCs

Verified:
- make proto-gen succeeds
- go build ./public/pluginapi/grpc/generated/go/pluginapiv1/... succeeds

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:33:52 -05:00
Nick Misasi
079fd4cd61 feat(03-02): add message hook RPCs to PluginHooks service
Add 12 message-related RPC definitions to the PluginHooks service:
- MessageWillBePosted - intercept/modify posts before save
- MessageWillBeUpdated - intercept/modify post updates before save
- MessageHasBeenPosted - notification after post created
- MessageHasBeenUpdated - notification after post updated
- MessagesWillBeConsumed - filter posts before client delivery
- MessageHasBeenDeleted - notification after post deleted
- FileWillBeUploaded - intercept/modify file uploads
- ReactionHasBeenAdded - notification after reaction added
- ReactionHasBeenRemoved - notification after reaction removed
- NotificationWillBePushed - intercept/modify push notifications
- EmailNotificationWillBeSent - intercept/modify email notifications
- PreferencesHaveChanged - notification after preferences changed

Each RPC includes documentation explaining:
- When the hook is called
- How to use return values (modify, reject, allow)
- The original Go signature

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:32:27 -05:00
Nick Misasi
ab912b3a2d feat(03-02): create hooks_message.proto with message hook definitions
Add protobuf message definitions for 12 message-related hooks:
- MessageWillBePosted, MessageWillBeUpdated
- MessageHasBeenPosted, MessageHasBeenUpdated
- MessagesWillBeConsumed, MessageHasBeenDeleted
- FileWillBeUploaded
- ReactionHasBeenAdded, ReactionHasBeenRemoved
- NotificationWillBePushed, EmailNotificationWillBeSent
- PreferencesHaveChanged

Also defines model types:
- PushNotification (typed proto, moderate size)
- EmailNotificationJson (JSON blob wrapper for complex type)
- EmailNotificationContent (typed return for email hook)
- Preference (simple 4-field struct)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:31:51 -05:00
Nick Misasi
54d0d6cc8b feat(02-01): add PluginAPI proto skeleton and parity verifier
- Create api.proto with PluginAPI service definition covering all 236
  methods from server/public/plugin/api.go
- Create api_user_team.proto with User, Session, and Team request/response
  messages with placeholder fields
- Create api_channel_post.proto with Channel, Post, and Emoji messages
- Create api_kv_config.proto with KV store, config, plugin, and logging
  messages
- Create api_file_bot.proto with File, Upload, and Bot messages
- Create api_remaining.proto with Server, Command, Preference, OAuth,
  Group, SharedChannel, Property, and Audit messages
- Add ViewUsersRestrictions to common.proto
- Add apiverify tool that parses Go API interface and proto service
  to ensure parity
- Update Makefile with new proto file mappings

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 11:24:09 -05:00
Nick Misasi
8a38e38f0d feat(03-01): add lifecycle/system hook RPCs in hooks_lifecycle.proto and hooks.proto
Define protobuf messages and PluginHooks gRPC service for lifecycle and
system hooks:
- Implemented: returns list of hooks the plugin implements
- OnActivate/OnDeactivate: plugin lifecycle events
- OnConfigurationChange: configuration change notifications
- OnInstall: plugin installation event
- OnSendDailyTelemetry: daily telemetry hook
- RunDataRetention: data retention batch processing
- OnCloudLimitsUpdated: cloud product limit changes
- ConfigurationWillBeSaved: configuration validation/modification

Also adds model types:
- OnInstallEvent: mirrors model.OnInstallEvent
- ProductLimits/FilesLimits/MessagesLimits/TeamsLimits: typed cloud limits
- ConfigJson: JSON blob wrapper for model.Config (too large for typed proto)

Updates Makefile to include new proto file mappings.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 11:18:19 -05:00
Nick Misasi
99f4b588a8 feat(03-01): add shared hook context types (PluginContext) in hooks_common.proto
Add hooks_common.proto with PluginContext message that mirrors the
server/public/plugin/context.go Context struct. This provides session_id,
request_id, ip_address, accept_language, and user_agent fields for passing
hook invocation context from server to plugins.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 11:16:06 -05:00
Nick Misasi
1a8f3c0c4e feat(01-03): add error handling and RPC envelope conventions
- Add RequestContext message for request metadata (plugin_id, request_id,
  session_id, user_id) to enable logging correlation across Go/Python
- Add params field (google.protobuf.Struct) to AppError for error message
  interpolation, matching model.AppError's params field
- Document RPC envelope conventions in proto comments:
  - Request messages: {Method}Request with RequestContext as field 1
  - Response messages: {Method}Response with AppError as field 1
  - Error strategy: Response-embedded AppError (Option B) for full
    semantic preservation, gRPC status reserved for transport errors
- Regenerate Go code with new types

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 11:10:27 -05:00
Nick Misasi
ca57bca5a2 feat(01-02): add core protobuf types (User, Channel, Post, Team, FileInfo)
- Add common.proto with Empty, StringMap, and AppError messages
- Add user.proto with User message (34 fields mirroring model.User)
- Add channel.proto with Channel, ChannelType enum, ChannelBannerInfo
- Add post.proto with Post, PostMetadata, PostEmbed, PostPriority, Reaction
- Add team.proto with Team, TeamMember, TeamUnread, TeamType enum
- Add file.proto with FileInfo, FileUploadResponse, FileData
- Update Makefile to support all proto files with proper import mappings
- Remove bootstrap.proto (replaced by common.proto)
- Use google.protobuf.Struct for dynamic JSON fields (Post.props, Channel.props)
- All timestamps are int64 (milliseconds since epoch)
- All IDs are strings (26-char base32)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 11:06:42 -05:00
Nick Misasi
2e61b0e626 feat(01-01): add protobuf directory structure and bootstrap.proto
- Create server/public/pluginapi/grpc/proto/ for proto sources
- Create server/public/pluginapi/grpc/generated/go/ for generated Go code
- Add bootstrap.proto with Empty message for validating codegen pipeline
- Generate bootstrap.pb.go using protoc v6.33.4 / protoc-gen-go v1.36.6

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 10:59:17 -05:00
Ben Schumacher
97dedb9de5
Migrate from gopkg.in/yaml.v3 to github.com/goccy/go-yaml (#34510)
Some checks are pending
API / build (push) Waiting to run
Server CI / Compute Go Version (push) Waiting to run
Server CI / Check mocks (push) Blocked by required conditions
Server CI / Check go mod tidy (push) Blocked by required conditions
Server CI / check-style (push) Blocked by required conditions
Server CI / Check serialization methods for hot structs (push) Blocked by required conditions
Server CI / Vet API (push) Blocked by required conditions
Server CI / Check migration files (push) Blocked by required conditions
Server CI / Generate email templates (push) Blocked by required conditions
Server CI / Check store layers (push) Blocked by required conditions
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres with binary parameters (push) Blocked by required conditions
Server CI / Postgres (push) Blocked by required conditions
Server CI / Postgres (FIPS) (push) Blocked by required conditions
Server CI / Generate Test Coverage (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Waiting to run
Web App CI / check-types (push) Waiting to run
Web App CI / test (push) Waiting to run
Web App CI / build (push) Waiting to run
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-18 08:52:05 +01:00
Julien Tant
78050bb0d3
Change properties search signature to support multiple TargetIDs (#33873)
* change properties search

* add tests

* Fix calls to to the search methods

* Fix SearchPropertyFields call with wrong signature
2025-09-11 22:56:01 +00:00
Guillermo Vayá
d15b933888
[MM-64683] Implement property field counting functionality in Plugin API (#33438)
* Implement property field limit enforcement and counting functionality in Plugin API

- Added a limit of 20 property fields per group in the CreatePropertyField method.
- Introduced CountPropertyFields method to count active and all property fields, including deleted ones.
- Enhanced tests to validate the new property field limit and counting behavior.
- Updated related API and service methods to support the new functionality.

* Update server/channels/app/properties/property_field.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix vet

* fix lint error

* fix test

* fix tests

* fix test

* count properties + targets

* Update server/channels/app/plugin_api.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* remove test for limit

* fix more tests

* improve testing messages now that the limit is removed

* Apply suggestion from @calebroseland

Co-authored-by: Caleb Roseland <caleb@calebroseland.com>

* Apply suggestion from @calebroseland

Co-authored-by: Caleb Roseland <caleb@calebroseland.com>

* Apply suggestion from @calebroseland

Co-authored-by: Caleb Roseland <caleb@calebroseland.com>

* Apply suggestion from @calebroseland

Co-authored-by: Caleb Roseland <caleb@calebroseland.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Julien Tant <785518+JulienTant@users.noreply.github.com>
Co-authored-by: Caleb Roseland <caleb@calebroseland.com>
2025-09-11 12:49:14 -07:00
Jesse Hallam
8cace74692
MM-64486: Remove telemetry (#33606)
* MM-64486: Remove telemetry

Remove telemetry from Mattermost. We're no longer relying on Rudder upstream, and no longer making use of this information.

* recover mock for SystemStore.Get

* Fix TestClearPushNotificationSync by adding missing SystemStore mock

The test was failing because the SystemStore mock was missing the Get()
method that's required by the ServerId() function. Added the missing mock
to return a StringMap with SystemServerId.

* fix mocking issue

* Remove now-unused telemetry and constants

* Remove "Disable telemetry events" debug setting

* Remove empty functions

* Remove most "Telemetry tracking removed" comments

* Remove remains of DataPrefetch telemetry

* Remove now-unused prop from InviteMembersButton

* Remove trackDotMenuEvent

* Remove some more leftover comments

* Remove lingering logic related to trackingLocation

* Remove now-unused argument from useCopyText

* Remove lingering telemetry references from PreparingWorkspace

* fixup Remove trackDotMenuEvent

* Remove lingering telemetry references from signup page and password check

* Update snapshots and fix test broken by my changes

* Fix unintended behavior change in thread list filtering

Remove handleSetFilter wrapper that was accidentally modified during
telemetry removal. The function was calling clear() when switching to
unread filter, which was not the original behavior. Use setFilter
directly instead, restoring the original functionality.

* Remove unused useOpenDowngradeModal hook

The useOpenDowngradeModal hook was not being used anywhere in the codebase.

* Remove unused expandableLink from useExpandOverageUsersCheck

The expandableLink return value was not being used by any components.

* Re-add missing TeamLinkClicked performance telemetry

The mark(Mark.TeamLinkClicked) call was accidentally removed from the
handleSwitch function. This telemetry is needed for Looker-based
performance tracking.

* drop LogSettings.VerboseDiagnostics

---------

Co-authored-by: Harrison Healey <harrisonmhealey@gmail.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2025-09-04 18:46:18 +00:00
Agniva De Sarker
9dd8c056e7
MM-63368: Remove MySQL (#33458)
https://mattermost.atlassian.net/browse/MM-63368

```release-note
Remove MySQL support from the codebase entirely.
```
2025-07-22 20:40:55 +05:30
Ben Schumacher
9add320011
[MM-64654] Migrate to modern Go features (#31820) 2025-07-18 12:54:51 +02:00
Julien Tant
731bd7c414
MM-63285: Add property field methods to plugin API (#31035)
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-10 16:10:28 -07:00
Ben Cooke
bfe90c3704
New pluginapi method for syncables (#30790)
* new pluginapi method for syncables
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
2025-05-21 14:44:34 -04:00
Harshil Sharma
a76c063d85
Renamed premium SKU to Enterprise Advanced (#30882) 2025-05-02 11:34:46 +05:30
Harshil Sharma
a9f09cadc2
Premium SKU (#30396)
* Added premium SKU

* removed duplicate enterprise license check functions

* Added license check on API layer

* lint fix

* lint fix

* refactured signature:

* test: Add comprehensive tests for license tier check functions

* fixed test

* text update

* optimised license checks

* fixedf test

* Updated license valid function

* webapp license checks

* handling prekium SKU in webappp:

* added plugin api method and general refactoring

* Updated tests

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2025-04-03 13:07:54 +05:30
Ben Schumacher
166a676fe5
Enforce use of any instead of interface{} (#30588) 2025-03-31 10:44:34 +02:00
Ben Schumacher
9b5d8d52bf
[MM-62427] Add message attachments validation (#30180)
* Add message attachments validation

* Add props validation

* Validate slack attachment fields

* Update tests and library usage

* Improve interactive dialog error for length checks

* Allow predefined colors for slack attachments

* Fix TestPostAction

* Use const for data source

* Add tests

* Cleanup unused props

* Add happy path tests

* lint fixes

* Add validation for PostActionOptions
2025-03-20 12:53:50 +01:00
Alejandro García Montoro
350714f390
Bump Go to v1.23.7 (#30455)
* Update Go version to v1.23.7

* Bump golangci-lint to a version supporting Go 1.23

* Fix golangci-lint warnings

Several rules from gosimple, revive and staticcheck linters were
failing:
- Redefinition of built-in identifiers (max, min, new, recover...)
- Use of printf-like functions with simple strings
- Check for nil slices, when len already takes it into account

* Trigger Build

* Trigger Build

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2025-03-17 14:22:07 +01:00
Ben Cooke
ccd8a60168
Plugin groups (#30320)
* add new pluginapi methods

* SAML login hook

* set ReAddRemovedMembers to true for plugin groups

* change to DoLogin signature for SAML
2025-03-13 12:00:15 -04:00
Agniva De Sarker
ac10bb12a5
Revert "Bump Go version to 1.23.6 (#30242)" (#30359)
This reverts commit acbbd4c58d.
2025-02-27 14:58:51 +05:30
Alejandro García Montoro
acbbd4c58d
Bump Go version to 1.23.6 (#30242)
* Bump Go version to 1.23.6

* Update CodeQL Github action as well

* Use server's Go version for CodeQL action

Co-authored-by: Antonis Stamatiou <stamatiou.antonis@gmail.com>

* Empty commit to trigger CI

* Bump golangci-lint to a version supporting Go 1.23

* Fix golangci-lint warnings

Several rules from gosimple, revive and staticcheck linters were
failing:
- Redefinition of built-in identifiers (max, min, new, recover...)
- Use of printf-like functions with simple strings
- Check for nil slices, when len already takes it into account

---------

Co-authored-by: Antonis Stamatiou <stamatiou.antonis@gmail.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2025-02-26 16:43:04 +01:00
Ben Schumacher
8d4bf4bae0
[MM-54288] Support Packet V2 (#29403) 2025-01-13 20:23:09 +01:00
Thomas Decaux
b61580f87f
fix(pluginapi/store): init master db if no replica (#29674)
* fix(pluginapi/store): init master db if no replica

GetReplicaDB return nil because masterDB is not initialized.

* chore: add test
2025-01-02 13:51:22 +05:30
Agniva De Sarker
3db139f746
Removed the max/min family of functions (#28679)
They are in-built in Go now
```release-note
NONE
```
2024-10-10 22:14:21 +05:30
Agniva De Sarker
c3ed07e679
OSF: Used model.NewPointer everywhere (#27838)
```release-note
NONE
```
2024-08-06 09:15:00 +05:30
Ben Schumacher
ff3ed78124
[MM-59292] Add metadata to Support Packet (#27573) 2024-07-14 20:26:43 +02:00
Ben Schumacher
6bbf7bbb9f
Update Packet metadata generation based on feedback (#27490) 2024-07-04 21:56:38 +02:00
Julien Tant
e96db725ea
PluginAPI: add ability to retrieve users by ids (#26936)
* pluginapi: ability to retrieve users by ids

* fix test
2024-05-15 07:06:40 -07:00
Agniva De Sarker
dc3e5b9269
MM-57532: Improve timeouts for some tests (#26642)
- Bump timeouts for some cases
- Improve sleep with require/assert.EventuallyT
for better performance and reliability.

https://mattermost.atlassian.net/browse/MM-57532

```release-note
NONE
```
2024-04-05 19:28:49 +05:30
Ben Schumacher
1e0de8f559
[MM-57356] Make use of go1.21 features (#26620) 2024-04-04 13:44:03 +02:00
Ben Schumacher
e3d86984cd
[MM-54994] Use local requests instead of HTTP requests in the flow library (#26471) 2024-04-04 10:28:31 +02:00
Jesse Hallam
acbaf4c283
add UpdateUserRoles to plugin api (#26615)
* ProfileImageBytes for EnsureBotOptions

* leverage plugintest.NewAPI

* fix linting

* add UpdateUserRoles to plugin api
2024-03-29 19:22:54 -03:00
Jesse Hallam
22c978e223
Profile image from bytes (#26610)
* ProfileImageBytes for EnsureBotOptions

* leverage plugintest.NewAPI

* fix linting

* clarify ProfileImage* godoc
2024-03-28 10:56:55 +00:00
Agniva De Sarker
89f5a0deec
MM-56402: Initialize replica conn pool on-demand (#26410)
Previously, we would setup both pools only when
GetMasterDB was called. This was inefficient and
would waste open connections if the replica wasn't used
at all.

We fix it to initialize the pools as they are called.

https://mattermost.atlassian.net/browse/MM-56402
```release-note
NONE
```

Co-authored-by: Jesse Hallam <jesse.hallam@gmail.com>
2024-03-08 13:11:48 +05:30
Ben Schumacher
02379b17ca
Implement plugin KV store in memory (#26244) 2024-03-01 12:12:52 +01:00
Ben Schumacher
de3e5aab25
[MM-53156] Remove Multi-Product architecture (#25669) 2024-02-15 13:01:44 +01:00
Ben Schumacher
5d3ba7483b
[MM-53057] Disallow malformed channel names for DMs (#24404) 2023-10-24 15:08:14 +02:00
Agniva De Sarker
3dd9e3715c
Goodbye, GraphQL (#24827)
```release-note
NONE
```
2023-10-12 09:47:35 +05:30
Jesús Espino
c9d49536f8
Adding SetFileSearchableContent plugin API endpoint (#24355)
* Adding SetFileSearchableContent plugin API endpoint

* Fixing CI problems

* Fixing CI problems

* Fixing CI problems

* Fixing CI problems

* Fixing CI problems

* Exposing it to the public API

* Fix CI problems

* Adding SetSearchableContent to the pluginapi File struct
2023-08-30 13:43:40 -07:00
Ben Schumacher
2108517fae
[MM-54014] Run tests in public/... (#24320) 2023-08-22 12:48:53 +02:00