Passed
Push — master ( b62c49...a3e854 )
by Joas
16:05 queued 12s
created
apps/user_status/appinfo/routes.php 1 patch
Indentation   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -24,20 +24,20 @@
 block discarded – undo
24 24
  *
25 25
  */
26 26
 return [
27
-	'ocs' => [
28
-		// Routes for querying statuses
29
-		['name' => 'Statuses#findAll', 'url' => '/api/v1/statuses', 'verb' => 'GET'],
30
-		['name' => 'Statuses#find', 'url' => '/api/v1/statuses/{userId}', 'verb' => 'GET'],
31
-		// Routes for manipulating your own status
32
-		['name' => 'UserStatus#getStatus', 'url' => '/api/v1/user_status', 'verb' => 'GET'],
33
-		['name' => 'UserStatus#setStatus', 'url' => '/api/v1/user_status/status', 'verb' => 'PUT'],
34
-		['name' => 'UserStatus#setPredefinedMessage', 'url' => '/api/v1/user_status/message/predefined', 'verb' => 'PUT'],
35
-		['name' => 'UserStatus#setCustomMessage', 'url' => '/api/v1/user_status/message/custom', 'verb' => 'PUT'],
36
-		['name' => 'UserStatus#clearMessage', 'url' => '/api/v1/user_status/message', 'verb' => 'DELETE'],
37
-		['name' => 'UserStatus#revertStatus', 'url' => '/api/v1/user_status/revert/{messageId}', 'verb' => 'DELETE'],
38
-		// Routes for listing default routes
39
-		['name' => 'PredefinedStatus#findAll', 'url' => '/api/v1/predefined_statuses/', 'verb' => 'GET'],
40
-		// Route for doing heartbeats
41
-		['name' => 'Heartbeat#heartbeat', 'url' => '/api/v1/heartbeat', 'verb' => 'PUT'],
42
-	],
27
+    'ocs' => [
28
+        // Routes for querying statuses
29
+        ['name' => 'Statuses#findAll', 'url' => '/api/v1/statuses', 'verb' => 'GET'],
30
+        ['name' => 'Statuses#find', 'url' => '/api/v1/statuses/{userId}', 'verb' => 'GET'],
31
+        // Routes for manipulating your own status
32
+        ['name' => 'UserStatus#getStatus', 'url' => '/api/v1/user_status', 'verb' => 'GET'],
33
+        ['name' => 'UserStatus#setStatus', 'url' => '/api/v1/user_status/status', 'verb' => 'PUT'],
34
+        ['name' => 'UserStatus#setPredefinedMessage', 'url' => '/api/v1/user_status/message/predefined', 'verb' => 'PUT'],
35
+        ['name' => 'UserStatus#setCustomMessage', 'url' => '/api/v1/user_status/message/custom', 'verb' => 'PUT'],
36
+        ['name' => 'UserStatus#clearMessage', 'url' => '/api/v1/user_status/message', 'verb' => 'DELETE'],
37
+        ['name' => 'UserStatus#revertStatus', 'url' => '/api/v1/user_status/revert/{messageId}', 'verb' => 'DELETE'],
38
+        // Routes for listing default routes
39
+        ['name' => 'PredefinedStatus#findAll', 'url' => '/api/v1/predefined_statuses/', 'verb' => 'GET'],
40
+        // Route for doing heartbeats
41
+        ['name' => 'Heartbeat#heartbeat', 'url' => '/api/v1/heartbeat', 'verb' => 'PUT'],
42
+    ],
43 43
 ];
Please login to merge, or discard this patch.
apps/user_status/lib/Controller/UserStatusController.php 1 patch
Indentation   +169 added lines, -169 removed lines patch added patch discarded remove patch
@@ -44,173 +44,173 @@
 block discarded – undo
44 44
 
45 45
 class UserStatusController extends OCSController {
46 46
 
47
-	/** @var string */
48
-	private $userId;
49
-
50
-	/** @var ILogger */
51
-	private $logger;
52
-
53
-	/** @var StatusService */
54
-	private $service;
55
-
56
-	/**
57
-	 * StatusesController constructor.
58
-	 *
59
-	 * @param string $appName
60
-	 * @param IRequest $request
61
-	 * @param string $userId
62
-	 * @param ILogger $logger;
63
-	 * @param StatusService $service
64
-	 */
65
-	public function __construct(string $appName,
66
-								IRequest $request,
67
-								string $userId,
68
-								ILogger $logger,
69
-								StatusService $service) {
70
-		parent::__construct($appName, $request);
71
-		$this->userId = $userId;
72
-		$this->logger = $logger;
73
-		$this->service = $service;
74
-	}
75
-
76
-	/**
77
-	 * @NoAdminRequired
78
-	 *
79
-	 * @return DataResponse
80
-	 * @throws OCSNotFoundException
81
-	 */
82
-	public function getStatus(): DataResponse {
83
-		try {
84
-			$userStatus = $this->service->findByUserId($this->userId);
85
-		} catch (DoesNotExistException $ex) {
86
-			throw new OCSNotFoundException('No status for the current user');
87
-		}
88
-
89
-		return new DataResponse($this->formatStatus($userStatus));
90
-	}
91
-
92
-	/**
93
-	 * @NoAdminRequired
94
-	 *
95
-	 * @param string $statusType
96
-	 * @return DataResponse
97
-	 * @throws OCSBadRequestException
98
-	 */
99
-	public function setStatus(string $statusType): DataResponse {
100
-		try {
101
-			$status = $this->service->setStatus($this->userId, $statusType, null, true);
102
-
103
-			$this->service->removeBackupUserStatus($this->userId);
104
-			return new DataResponse($this->formatStatus($status));
105
-		} catch (InvalidStatusTypeException $ex) {
106
-			$this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid status type "' . $statusType . '"');
107
-			throw new OCSBadRequestException($ex->getMessage(), $ex);
108
-		}
109
-	}
110
-
111
-	/**
112
-	 * @NoAdminRequired
113
-	 *
114
-	 * @param string $messageId
115
-	 * @param int|null $clearAt
116
-	 * @return DataResponse
117
-	 * @throws OCSBadRequestException
118
-	 */
119
-	public function setPredefinedMessage(string $messageId,
120
-										 ?int $clearAt): DataResponse {
121
-		try {
122
-			$status = $this->service->setPredefinedMessage($this->userId, $messageId, $clearAt);
123
-			$this->service->removeBackupUserStatus($this->userId);
124
-			return new DataResponse($this->formatStatus($status));
125
-		} catch (InvalidClearAtException $ex) {
126
-			$this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid clearAt value "' . $clearAt . '"');
127
-			throw new OCSBadRequestException($ex->getMessage(), $ex);
128
-		} catch (InvalidMessageIdException $ex) {
129
-			$this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid message-id "' . $messageId . '"');
130
-			throw new OCSBadRequestException($ex->getMessage(), $ex);
131
-		}
132
-	}
133
-
134
-	/**
135
-	 * @NoAdminRequired
136
-	 *
137
-	 * @param string|null $statusIcon
138
-	 * @param string|null $message
139
-	 * @param int|null $clearAt
140
-	 * @return DataResponse
141
-	 * @throws OCSBadRequestException
142
-	 */
143
-	public function setCustomMessage(?string $statusIcon,
144
-									 ?string $message,
145
-									 ?int $clearAt): DataResponse {
146
-		try {
147
-			if (($message !== null && $message !== '') || ($clearAt !== null && $clearAt !== 0)) {
148
-				$status = $this->service->setCustomMessage($this->userId, $statusIcon, $message, $clearAt);
149
-			} else {
150
-				$this->service->clearMessage($this->userId);
151
-				$status = $this->service->findByUserId($this->userId);
152
-			}
153
-			$this->service->removeBackupUserStatus($this->userId);
154
-			return new DataResponse($this->formatStatus($status));
155
-		} catch (InvalidClearAtException $ex) {
156
-			$this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid clearAt value "' . $clearAt . '"');
157
-			throw new OCSBadRequestException($ex->getMessage(), $ex);
158
-		} catch (InvalidStatusIconException $ex) {
159
-			$this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid icon value "' . $statusIcon . '"');
160
-			throw new OCSBadRequestException($ex->getMessage(), $ex);
161
-		} catch (StatusMessageTooLongException $ex) {
162
-			$this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to a too long status message.');
163
-			throw new OCSBadRequestException($ex->getMessage(), $ex);
164
-		}
165
-	}
166
-
167
-	/**
168
-	 * @NoAdminRequired
169
-	 *
170
-	 * @return DataResponse
171
-	 */
172
-	public function clearStatus(): DataResponse {
173
-		$this->service->clearStatus($this->userId);
174
-		return new DataResponse([]);
175
-	}
176
-
177
-	/**
178
-	 * @NoAdminRequired
179
-	 *
180
-	 * @return DataResponse
181
-	 */
182
-	public function clearMessage(): DataResponse {
183
-		$this->service->clearMessage($this->userId);
184
-		return new DataResponse([]);
185
-	}
186
-
187
-	/**
188
-	 * @NoAdminRequired
189
-	 *
190
-	 * @return DataResponse
191
-	 */
192
-	public function revertStatus(string $messageId): DataResponse {
193
-		$backupStatus = $this->service->revertUserStatus($this->userId, $messageId, true);
194
-		if ($backupStatus) {
195
-			return new DataResponse($this->formatStatus($backupStatus));
196
-		}
197
-		return new DataResponse([]);
198
-	}
199
-
200
-	/**
201
-	 * @param UserStatus $status
202
-	 * @return array
203
-	 */
204
-	private function formatStatus(UserStatus $status): array {
205
-		return [
206
-			'userId' => $status->getUserId(),
207
-			'message' => $status->getCustomMessage(),
208
-			'messageId' => $status->getMessageId(),
209
-			'messageIsPredefined' => $status->getMessageId() !== null,
210
-			'icon' => $status->getCustomIcon(),
211
-			'clearAt' => $status->getClearAt(),
212
-			'status' => $status->getStatus(),
213
-			'statusIsUserDefined' => $status->getIsUserDefined(),
214
-		];
215
-	}
47
+    /** @var string */
48
+    private $userId;
49
+
50
+    /** @var ILogger */
51
+    private $logger;
52
+
53
+    /** @var StatusService */
54
+    private $service;
55
+
56
+    /**
57
+     * StatusesController constructor.
58
+     *
59
+     * @param string $appName
60
+     * @param IRequest $request
61
+     * @param string $userId
62
+     * @param ILogger $logger;
63
+     * @param StatusService $service
64
+     */
65
+    public function __construct(string $appName,
66
+                                IRequest $request,
67
+                                string $userId,
68
+                                ILogger $logger,
69
+                                StatusService $service) {
70
+        parent::__construct($appName, $request);
71
+        $this->userId = $userId;
72
+        $this->logger = $logger;
73
+        $this->service = $service;
74
+    }
75
+
76
+    /**
77
+     * @NoAdminRequired
78
+     *
79
+     * @return DataResponse
80
+     * @throws OCSNotFoundException
81
+     */
82
+    public function getStatus(): DataResponse {
83
+        try {
84
+            $userStatus = $this->service->findByUserId($this->userId);
85
+        } catch (DoesNotExistException $ex) {
86
+            throw new OCSNotFoundException('No status for the current user');
87
+        }
88
+
89
+        return new DataResponse($this->formatStatus($userStatus));
90
+    }
91
+
92
+    /**
93
+     * @NoAdminRequired
94
+     *
95
+     * @param string $statusType
96
+     * @return DataResponse
97
+     * @throws OCSBadRequestException
98
+     */
99
+    public function setStatus(string $statusType): DataResponse {
100
+        try {
101
+            $status = $this->service->setStatus($this->userId, $statusType, null, true);
102
+
103
+            $this->service->removeBackupUserStatus($this->userId);
104
+            return new DataResponse($this->formatStatus($status));
105
+        } catch (InvalidStatusTypeException $ex) {
106
+            $this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid status type "' . $statusType . '"');
107
+            throw new OCSBadRequestException($ex->getMessage(), $ex);
108
+        }
109
+    }
110
+
111
+    /**
112
+     * @NoAdminRequired
113
+     *
114
+     * @param string $messageId
115
+     * @param int|null $clearAt
116
+     * @return DataResponse
117
+     * @throws OCSBadRequestException
118
+     */
119
+    public function setPredefinedMessage(string $messageId,
120
+                                         ?int $clearAt): DataResponse {
121
+        try {
122
+            $status = $this->service->setPredefinedMessage($this->userId, $messageId, $clearAt);
123
+            $this->service->removeBackupUserStatus($this->userId);
124
+            return new DataResponse($this->formatStatus($status));
125
+        } catch (InvalidClearAtException $ex) {
126
+            $this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid clearAt value "' . $clearAt . '"');
127
+            throw new OCSBadRequestException($ex->getMessage(), $ex);
128
+        } catch (InvalidMessageIdException $ex) {
129
+            $this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid message-id "' . $messageId . '"');
130
+            throw new OCSBadRequestException($ex->getMessage(), $ex);
131
+        }
132
+    }
133
+
134
+    /**
135
+     * @NoAdminRequired
136
+     *
137
+     * @param string|null $statusIcon
138
+     * @param string|null $message
139
+     * @param int|null $clearAt
140
+     * @return DataResponse
141
+     * @throws OCSBadRequestException
142
+     */
143
+    public function setCustomMessage(?string $statusIcon,
144
+                                     ?string $message,
145
+                                     ?int $clearAt): DataResponse {
146
+        try {
147
+            if (($message !== null && $message !== '') || ($clearAt !== null && $clearAt !== 0)) {
148
+                $status = $this->service->setCustomMessage($this->userId, $statusIcon, $message, $clearAt);
149
+            } else {
150
+                $this->service->clearMessage($this->userId);
151
+                $status = $this->service->findByUserId($this->userId);
152
+            }
153
+            $this->service->removeBackupUserStatus($this->userId);
154
+            return new DataResponse($this->formatStatus($status));
155
+        } catch (InvalidClearAtException $ex) {
156
+            $this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid clearAt value "' . $clearAt . '"');
157
+            throw new OCSBadRequestException($ex->getMessage(), $ex);
158
+        } catch (InvalidStatusIconException $ex) {
159
+            $this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to an invalid icon value "' . $statusIcon . '"');
160
+            throw new OCSBadRequestException($ex->getMessage(), $ex);
161
+        } catch (StatusMessageTooLongException $ex) {
162
+            $this->logger->debug('New user-status for "' . $this->userId . '" was rejected due to a too long status message.');
163
+            throw new OCSBadRequestException($ex->getMessage(), $ex);
164
+        }
165
+    }
166
+
167
+    /**
168
+     * @NoAdminRequired
169
+     *
170
+     * @return DataResponse
171
+     */
172
+    public function clearStatus(): DataResponse {
173
+        $this->service->clearStatus($this->userId);
174
+        return new DataResponse([]);
175
+    }
176
+
177
+    /**
178
+     * @NoAdminRequired
179
+     *
180
+     * @return DataResponse
181
+     */
182
+    public function clearMessage(): DataResponse {
183
+        $this->service->clearMessage($this->userId);
184
+        return new DataResponse([]);
185
+    }
186
+
187
+    /**
188
+     * @NoAdminRequired
189
+     *
190
+     * @return DataResponse
191
+     */
192
+    public function revertStatus(string $messageId): DataResponse {
193
+        $backupStatus = $this->service->revertUserStatus($this->userId, $messageId, true);
194
+        if ($backupStatus) {
195
+            return new DataResponse($this->formatStatus($backupStatus));
196
+        }
197
+        return new DataResponse([]);
198
+    }
199
+
200
+    /**
201
+     * @param UserStatus $status
202
+     * @return array
203
+     */
204
+    private function formatStatus(UserStatus $status): array {
205
+        return [
206
+            'userId' => $status->getUserId(),
207
+            'message' => $status->getCustomMessage(),
208
+            'messageId' => $status->getMessageId(),
209
+            'messageIsPredefined' => $status->getMessageId() !== null,
210
+            'icon' => $status->getCustomIcon(),
211
+            'clearAt' => $status->getClearAt(),
212
+            'status' => $status->getStatus(),
213
+            'statusIsUserDefined' => $status->getIsUserDefined(),
214
+        ];
215
+    }
216 216
 }
Please login to merge, or discard this patch.
apps/user_status/lib/Service/StatusService.php 1 patch
Indentation   +508 added lines, -508 removed lines patch added patch discarded remove patch
@@ -47,512 +47,512 @@
 block discarded – undo
47 47
  */
48 48
 class StatusService {
49 49
 
50
-	/** @var UserStatusMapper */
51
-	private $mapper;
52
-
53
-	/** @var ITimeFactory */
54
-	private $timeFactory;
55
-
56
-	/** @var PredefinedStatusService */
57
-	private $predefinedStatusService;
58
-
59
-	private IEmojiHelper $emojiHelper;
60
-
61
-	/** @var bool */
62
-	private $shareeEnumeration;
63
-
64
-	/** @var bool */
65
-	private $shareeEnumerationInGroupOnly;
66
-
67
-	/** @var bool */
68
-	private $shareeEnumerationPhone;
69
-
70
-	/**
71
-	 * List of priorities ordered by their priority
72
-	 */
73
-	public const PRIORITY_ORDERED_STATUSES = [
74
-		IUserStatus::ONLINE,
75
-		IUserStatus::AWAY,
76
-		IUserStatus::DND,
77
-		IUserStatus::INVISIBLE,
78
-		IUserStatus::OFFLINE,
79
-	];
80
-
81
-	/**
82
-	 * List of statuses that persist the clear-up
83
-	 * or UserLiveStatusEvents
84
-	 */
85
-	public const PERSISTENT_STATUSES = [
86
-		IUserStatus::AWAY,
87
-		IUserStatus::DND,
88
-		IUserStatus::INVISIBLE,
89
-	];
90
-
91
-	/** @var int */
92
-	public const INVALIDATE_STATUS_THRESHOLD = 15 /* minutes */ * 60 /* seconds */;
93
-
94
-	/** @var int */
95
-	public const MAXIMUM_MESSAGE_LENGTH = 80;
96
-
97
-	public function __construct(UserStatusMapper $mapper,
98
-								ITimeFactory $timeFactory,
99
-								PredefinedStatusService $defaultStatusService,
100
-								IEmojiHelper $emojiHelper,
101
-								IConfig $config) {
102
-		$this->mapper = $mapper;
103
-		$this->timeFactory = $timeFactory;
104
-		$this->predefinedStatusService = $defaultStatusService;
105
-		$this->emojiHelper = $emojiHelper;
106
-		$this->shareeEnumeration = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
107
-		$this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
108
-		$this->shareeEnumerationPhone = $this->shareeEnumeration && $config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
109
-	}
110
-
111
-	/**
112
-	 * @param int|null $limit
113
-	 * @param int|null $offset
114
-	 * @return UserStatus[]
115
-	 */
116
-	public function findAll(?int $limit = null, ?int $offset = null): array {
117
-		// Return empty array if user enumeration is disabled or limited to groups
118
-		// TODO: find a solution that scales to get only users from common groups if user enumeration is limited to
119
-		//       groups. See discussion at https://github.com/nextcloud/server/pull/27879#discussion_r729715936
120
-		if (!$this->shareeEnumeration || $this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone) {
121
-			return [];
122
-		}
123
-
124
-		return array_map(function ($status) {
125
-			return $this->processStatus($status);
126
-		}, $this->mapper->findAll($limit, $offset));
127
-	}
128
-
129
-	/**
130
-	 * @param int|null $limit
131
-	 * @param int|null $offset
132
-	 * @return array
133
-	 */
134
-	public function findAllRecentStatusChanges(?int $limit = null, ?int $offset = null): array {
135
-		// Return empty array if user enumeration is disabled or limited to groups
136
-		// TODO: find a solution that scales to get only users from common groups if user enumeration is limited to
137
-		//       groups. See discussion at https://github.com/nextcloud/server/pull/27879#discussion_r729715936
138
-		if (!$this->shareeEnumeration || $this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone) {
139
-			return [];
140
-		}
141
-
142
-		return array_map(function ($status) {
143
-			return $this->processStatus($status);
144
-		}, $this->mapper->findAllRecent($limit, $offset));
145
-	}
146
-
147
-	/**
148
-	 * @param string $userId
149
-	 * @return UserStatus
150
-	 * @throws DoesNotExistException
151
-	 */
152
-	public function findByUserId(string $userId):UserStatus {
153
-		return $this->processStatus($this->mapper->findByUserId($userId));
154
-	}
155
-
156
-	/**
157
-	 * @param array $userIds
158
-	 * @return UserStatus[]
159
-	 */
160
-	public function findByUserIds(array $userIds):array {
161
-		return array_map(function ($status) {
162
-			return $this->processStatus($status);
163
-		}, $this->mapper->findByUserIds($userIds));
164
-	}
165
-
166
-	/**
167
-	 * @param string $userId
168
-	 * @param string $status
169
-	 * @param int|null $statusTimestamp
170
-	 * @param bool $isUserDefined
171
-	 * @return UserStatus
172
-	 * @throws InvalidStatusTypeException
173
-	 */
174
-	public function setStatus(string $userId,
175
-							  string $status,
176
-							  ?int $statusTimestamp,
177
-							  bool $isUserDefined): UserStatus {
178
-		try {
179
-			$userStatus = $this->mapper->findByUserId($userId);
180
-		} catch (DoesNotExistException $ex) {
181
-			$userStatus = new UserStatus();
182
-			$userStatus->setUserId($userId);
183
-		}
184
-
185
-		// Check if status-type is valid
186
-		if (!\in_array($status, self::PRIORITY_ORDERED_STATUSES, true)) {
187
-			throw new InvalidStatusTypeException('Status-type "' . $status . '" is not supported');
188
-		}
189
-		if ($statusTimestamp === null) {
190
-			$statusTimestamp = $this->timeFactory->getTime();
191
-		}
192
-
193
-		$userStatus->setStatus($status);
194
-		$userStatus->setStatusTimestamp($statusTimestamp);
195
-		$userStatus->setIsUserDefined($isUserDefined);
196
-		$userStatus->setIsBackup(false);
197
-
198
-		if ($userStatus->getId() === null) {
199
-			return $this->mapper->insert($userStatus);
200
-		}
201
-
202
-		return $this->mapper->update($userStatus);
203
-	}
204
-
205
-	/**
206
-	 * @param string $userId
207
-	 * @param string $messageId
208
-	 * @param int|null $clearAt
209
-	 * @return UserStatus
210
-	 * @throws InvalidMessageIdException
211
-	 * @throws InvalidClearAtException
212
-	 */
213
-	public function setPredefinedMessage(string $userId,
214
-										 string $messageId,
215
-										 ?int $clearAt): UserStatus {
216
-		try {
217
-			$userStatus = $this->mapper->findByUserId($userId);
218
-		} catch (DoesNotExistException $ex) {
219
-			$userStatus = new UserStatus();
220
-			$userStatus->setUserId($userId);
221
-			$userStatus->setStatus(IUserStatus::OFFLINE);
222
-			$userStatus->setStatusTimestamp(0);
223
-			$userStatus->setIsUserDefined(false);
224
-			$userStatus->setIsBackup(false);
225
-		}
226
-
227
-		if (!$this->predefinedStatusService->isValidId($messageId)) {
228
-			throw new InvalidMessageIdException('Message-Id "' . $messageId . '" is not supported');
229
-		}
230
-
231
-		// Check that clearAt is in the future
232
-		if ($clearAt !== null && $clearAt < $this->timeFactory->getTime()) {
233
-			throw new InvalidClearAtException('ClearAt is in the past');
234
-		}
235
-
236
-		$userStatus->setMessageId($messageId);
237
-		$userStatus->setCustomIcon(null);
238
-		$userStatus->setCustomMessage(null);
239
-		$userStatus->setClearAt($clearAt);
240
-
241
-		if ($userStatus->getId() === null) {
242
-			return $this->mapper->insert($userStatus);
243
-		}
244
-
245
-		return $this->mapper->update($userStatus);
246
-	}
247
-
248
-	/**
249
-	 * @param string $userId
250
-	 * @param string $status
251
-	 * @param string $messageId
252
-	 * @param bool $createBackup
253
-	 * @throws InvalidStatusTypeException
254
-	 * @throws InvalidMessageIdException
255
-	 */
256
-	public function setUserStatus(string $userId,
257
-										 string $status,
258
-										 string $messageId,
259
-										 bool $createBackup): void {
260
-		// Check if status-type is valid
261
-		if (!\in_array($status, self::PRIORITY_ORDERED_STATUSES, true)) {
262
-			throw new InvalidStatusTypeException('Status-type "' . $status . '" is not supported');
263
-		}
264
-
265
-		if (!$this->predefinedStatusService->isValidId($messageId)) {
266
-			throw new InvalidMessageIdException('Message-Id "' . $messageId . '" is not supported');
267
-		}
268
-
269
-		if ($createBackup) {
270
-			if ($this->backupCurrentStatus($userId) === false) {
271
-				return; // Already a status set automatically => abort.
272
-			}
273
-
274
-			// If we just created the backup
275
-			$userStatus = new UserStatus();
276
-			$userStatus->setUserId($userId);
277
-		} else {
278
-			try {
279
-				$userStatus = $this->mapper->findByUserId($userId);
280
-			} catch (DoesNotExistException $ex) {
281
-				$userStatus = new UserStatus();
282
-				$userStatus->setUserId($userId);
283
-			}
284
-		}
285
-
286
-		$userStatus->setStatus($status);
287
-		$userStatus->setStatusTimestamp($this->timeFactory->getTime());
288
-		$userStatus->setIsUserDefined(true);
289
-		$userStatus->setIsBackup(false);
290
-		$userStatus->setMessageId($messageId);
291
-		$userStatus->setCustomIcon(null);
292
-		$userStatus->setCustomMessage(null);
293
-		$userStatus->setClearAt(null);
294
-
295
-		if ($userStatus->getId() !== null) {
296
-			$this->mapper->update($userStatus);
297
-			return;
298
-		}
299
-		$this->mapper->insert($userStatus);
300
-	}
301
-
302
-	/**
303
-	 * @param string $userId
304
-	 * @param string|null $statusIcon
305
-	 * @param string|null $message
306
-	 * @param int|null $clearAt
307
-	 * @return UserStatus
308
-	 * @throws InvalidClearAtException
309
-	 * @throws InvalidStatusIconException
310
-	 * @throws StatusMessageTooLongException
311
-	 */
312
-	public function setCustomMessage(string $userId,
313
-									 ?string $statusIcon,
314
-									 ?string $message,
315
-									 ?int $clearAt): UserStatus {
316
-		try {
317
-			$userStatus = $this->mapper->findByUserId($userId);
318
-		} catch (DoesNotExistException $ex) {
319
-			$userStatus = new UserStatus();
320
-			$userStatus->setUserId($userId);
321
-			$userStatus->setStatus(IUserStatus::OFFLINE);
322
-			$userStatus->setStatusTimestamp(0);
323
-			$userStatus->setIsUserDefined(false);
324
-		}
325
-
326
-		// Check if statusIcon contains only one character
327
-		if ($statusIcon !== null && !$this->emojiHelper->isValidSingleEmoji($statusIcon)) {
328
-			throw new InvalidStatusIconException('Status-Icon is longer than one character');
329
-		}
330
-		// Check for maximum length of custom message
331
-		if ($message !== null && \mb_strlen($message) > self::MAXIMUM_MESSAGE_LENGTH) {
332
-			throw new StatusMessageTooLongException('Message is longer than supported length of ' . self::MAXIMUM_MESSAGE_LENGTH . ' characters');
333
-		}
334
-		// Check that clearAt is in the future
335
-		if ($clearAt !== null && $clearAt < $this->timeFactory->getTime()) {
336
-			throw new InvalidClearAtException('ClearAt is in the past');
337
-		}
338
-
339
-		$userStatus->setMessageId(null);
340
-		$userStatus->setCustomIcon($statusIcon);
341
-		$userStatus->setCustomMessage($message);
342
-		$userStatus->setClearAt($clearAt);
343
-
344
-		if ($userStatus->getId() === null) {
345
-			return $this->mapper->insert($userStatus);
346
-		}
347
-
348
-		return $this->mapper->update($userStatus);
349
-	}
350
-
351
-	/**
352
-	 * @param string $userId
353
-	 * @return bool
354
-	 */
355
-	public function clearStatus(string $userId): bool {
356
-		try {
357
-			$userStatus = $this->mapper->findByUserId($userId);
358
-		} catch (DoesNotExistException $ex) {
359
-			// if there is no status to remove, just return
360
-			return false;
361
-		}
362
-
363
-		$userStatus->setStatus(IUserStatus::OFFLINE);
364
-		$userStatus->setStatusTimestamp(0);
365
-		$userStatus->setIsUserDefined(false);
366
-
367
-		$this->mapper->update($userStatus);
368
-		return true;
369
-	}
370
-
371
-	/**
372
-	 * @param string $userId
373
-	 * @return bool
374
-	 */
375
-	public function clearMessage(string $userId): bool {
376
-		try {
377
-			$userStatus = $this->mapper->findByUserId($userId);
378
-		} catch (DoesNotExistException $ex) {
379
-			// if there is no status to remove, just return
380
-			return false;
381
-		}
382
-
383
-		$userStatus->setMessageId(null);
384
-		$userStatus->setCustomMessage(null);
385
-		$userStatus->setCustomIcon(null);
386
-		$userStatus->setClearAt(null);
387
-
388
-		$this->mapper->update($userStatus);
389
-		return true;
390
-	}
391
-
392
-	/**
393
-	 * @param string $userId
394
-	 * @return bool
395
-	 */
396
-	public function removeUserStatus(string $userId): bool {
397
-		try {
398
-			$userStatus = $this->mapper->findByUserId($userId, false);
399
-		} catch (DoesNotExistException $ex) {
400
-			// if there is no status to remove, just return
401
-			return false;
402
-		}
403
-
404
-		$this->mapper->delete($userStatus);
405
-		return true;
406
-	}
407
-
408
-	public function removeBackupUserStatus(string $userId): bool {
409
-		try {
410
-			$userStatus = $this->mapper->findByUserId($userId, true);
411
-		} catch (DoesNotExistException $ex) {
412
-			// if there is no status to remove, just return
413
-			return false;
414
-		}
415
-
416
-		$this->mapper->delete($userStatus);
417
-		return true;
418
-	}
419
-
420
-	/**
421
-	 * Processes a status to check if custom message is still
422
-	 * up to date and provides translated default status if needed
423
-	 *
424
-	 * @param UserStatus $status
425
-	 * @return UserStatus
426
-	 */
427
-	private function processStatus(UserStatus $status): UserStatus {
428
-		$clearAt = $status->getClearAt();
429
-
430
-		if ($status->getStatusTimestamp() < $this->timeFactory->getTime() - self::INVALIDATE_STATUS_THRESHOLD
431
-			&& (!$status->getIsUserDefined() || $status->getStatus() === IUserStatus::ONLINE)) {
432
-			$this->cleanStatus($status);
433
-		}
434
-		if ($clearAt !== null && $clearAt < $this->timeFactory->getTime()) {
435
-			$this->cleanStatus($status);
436
-			$this->cleanStatusMessage($status);
437
-		}
438
-		if ($status->getMessageId() !== null) {
439
-			$this->addDefaultMessage($status);
440
-		}
441
-
442
-		return $status;
443
-	}
444
-
445
-	/**
446
-	 * @param UserStatus $status
447
-	 */
448
-	private function cleanStatus(UserStatus $status): void {
449
-		if ($status->getStatus() === IUserStatus::OFFLINE && !$status->getIsUserDefined()) {
450
-			return;
451
-		}
452
-
453
-		$status->setStatus(IUserStatus::OFFLINE);
454
-		$status->setStatusTimestamp($this->timeFactory->getTime());
455
-		$status->setIsUserDefined(false);
456
-
457
-		$this->mapper->update($status);
458
-	}
459
-
460
-	/**
461
-	 * @param UserStatus $status
462
-	 */
463
-	private function cleanStatusMessage(UserStatus $status): void {
464
-		$status->setMessageId(null);
465
-		$status->setCustomIcon(null);
466
-		$status->setCustomMessage(null);
467
-		$status->setClearAt(null);
468
-
469
-		$this->mapper->update($status);
470
-	}
471
-
472
-	/**
473
-	 * @param UserStatus $status
474
-	 */
475
-	private function addDefaultMessage(UserStatus $status): void {
476
-		// If the message is predefined, insert the translated message and icon
477
-		$predefinedMessage = $this->predefinedStatusService->getDefaultStatusById($status->getMessageId());
478
-		if ($predefinedMessage !== null) {
479
-			$status->setCustomMessage($predefinedMessage['message']);
480
-			$status->setCustomIcon($predefinedMessage['icon']);
481
-		}
482
-	}
483
-
484
-	/**
485
-	 * @return bool false if there is already a backup. In this case abort the procedure.
486
-	 */
487
-	public function backupCurrentStatus(string $userId): bool {
488
-		try {
489
-			$this->mapper->createBackupStatus($userId);
490
-			return true;
491
-		} catch (Exception $ex) {
492
-			if ($ex->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
493
-				return false;
494
-			}
495
-			throw $ex;
496
-		}
497
-	}
498
-
499
-	public function revertUserStatus(string $userId, string $messageId, bool $revertedManually = false): ?UserStatus {
500
-		try {
501
-			/** @var UserStatus $userStatus */
502
-			$backupUserStatus = $this->mapper->findByUserId($userId, true);
503
-		} catch (DoesNotExistException $ex) {
504
-			// No user status to revert, do nothing
505
-			return null;
506
-		}
507
-
508
-		$deleted = $this->mapper->deleteCurrentStatusToRestoreBackup($userId, $messageId);
509
-		if (!$deleted) {
510
-			// Another status is set automatically or no status, do nothing
511
-			return null;
512
-		}
513
-
514
-		if ($revertedManually && $backupUserStatus->getStatus() === IUserStatus::OFFLINE) {
515
-			// When the user reverts the status manually they are online
516
-			$backupUserStatus->setStatus(IUserStatus::ONLINE);
517
-		}
518
-
519
-		$backupUserStatus->setIsBackup(false);
520
-		// Remove the underscore prefix added when creating the backup
521
-		$backupUserStatus->setUserId(substr($backupUserStatus->getUserId(), 1));
522
-		$this->mapper->update($backupUserStatus);
523
-
524
-		return $backupUserStatus;
525
-	}
526
-
527
-	public function revertMultipleUserStatus(array $userIds, string $messageId): void {
528
-		// Get all user statuses and the backups
529
-		$findById = $userIds;
530
-		foreach ($userIds as $userId) {
531
-			$findById[] = '_' . $userId;
532
-		}
533
-		$userStatuses = $this->mapper->findByUserIds($findById);
534
-
535
-		$backups = $restoreIds = $statuesToDelete = [];
536
-		foreach ($userStatuses as $userStatus) {
537
-			if (!$userStatus->getIsBackup()
538
-				&& $userStatus->getMessageId() === $messageId) {
539
-				$statuesToDelete[$userStatus->getUserId()] = $userStatus->getId();
540
-			} else if ($userStatus->getIsBackup()) {
541
-				$backups[$userStatus->getUserId()] = $userStatus->getId();
542
-			}
543
-		}
544
-
545
-		// For users with both (normal and backup) delete the status when matching
546
-		foreach ($statuesToDelete as $userId => $statusId) {
547
-			$backupUserId = '_' . $userId;
548
-			if (isset($backups[$backupUserId])) {
549
-				$restoreIds[] = $backups[$backupUserId];
550
-			}
551
-		}
552
-
553
-		$this->mapper->deleteByIds(array_values($statuesToDelete));
554
-
555
-		// For users that matched restore the previous status
556
-		$this->mapper->restoreBackupStatuses($restoreIds);
557
-	}
50
+    /** @var UserStatusMapper */
51
+    private $mapper;
52
+
53
+    /** @var ITimeFactory */
54
+    private $timeFactory;
55
+
56
+    /** @var PredefinedStatusService */
57
+    private $predefinedStatusService;
58
+
59
+    private IEmojiHelper $emojiHelper;
60
+
61
+    /** @var bool */
62
+    private $shareeEnumeration;
63
+
64
+    /** @var bool */
65
+    private $shareeEnumerationInGroupOnly;
66
+
67
+    /** @var bool */
68
+    private $shareeEnumerationPhone;
69
+
70
+    /**
71
+     * List of priorities ordered by their priority
72
+     */
73
+    public const PRIORITY_ORDERED_STATUSES = [
74
+        IUserStatus::ONLINE,
75
+        IUserStatus::AWAY,
76
+        IUserStatus::DND,
77
+        IUserStatus::INVISIBLE,
78
+        IUserStatus::OFFLINE,
79
+    ];
80
+
81
+    /**
82
+     * List of statuses that persist the clear-up
83
+     * or UserLiveStatusEvents
84
+     */
85
+    public const PERSISTENT_STATUSES = [
86
+        IUserStatus::AWAY,
87
+        IUserStatus::DND,
88
+        IUserStatus::INVISIBLE,
89
+    ];
90
+
91
+    /** @var int */
92
+    public const INVALIDATE_STATUS_THRESHOLD = 15 /* minutes */ * 60 /* seconds */;
93
+
94
+    /** @var int */
95
+    public const MAXIMUM_MESSAGE_LENGTH = 80;
96
+
97
+    public function __construct(UserStatusMapper $mapper,
98
+                                ITimeFactory $timeFactory,
99
+                                PredefinedStatusService $defaultStatusService,
100
+                                IEmojiHelper $emojiHelper,
101
+                                IConfig $config) {
102
+        $this->mapper = $mapper;
103
+        $this->timeFactory = $timeFactory;
104
+        $this->predefinedStatusService = $defaultStatusService;
105
+        $this->emojiHelper = $emojiHelper;
106
+        $this->shareeEnumeration = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
107
+        $this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
108
+        $this->shareeEnumerationPhone = $this->shareeEnumeration && $config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
109
+    }
110
+
111
+    /**
112
+     * @param int|null $limit
113
+     * @param int|null $offset
114
+     * @return UserStatus[]
115
+     */
116
+    public function findAll(?int $limit = null, ?int $offset = null): array {
117
+        // Return empty array if user enumeration is disabled or limited to groups
118
+        // TODO: find a solution that scales to get only users from common groups if user enumeration is limited to
119
+        //       groups. See discussion at https://github.com/nextcloud/server/pull/27879#discussion_r729715936
120
+        if (!$this->shareeEnumeration || $this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone) {
121
+            return [];
122
+        }
123
+
124
+        return array_map(function ($status) {
125
+            return $this->processStatus($status);
126
+        }, $this->mapper->findAll($limit, $offset));
127
+    }
128
+
129
+    /**
130
+     * @param int|null $limit
131
+     * @param int|null $offset
132
+     * @return array
133
+     */
134
+    public function findAllRecentStatusChanges(?int $limit = null, ?int $offset = null): array {
135
+        // Return empty array if user enumeration is disabled or limited to groups
136
+        // TODO: find a solution that scales to get only users from common groups if user enumeration is limited to
137
+        //       groups. See discussion at https://github.com/nextcloud/server/pull/27879#discussion_r729715936
138
+        if (!$this->shareeEnumeration || $this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone) {
139
+            return [];
140
+        }
141
+
142
+        return array_map(function ($status) {
143
+            return $this->processStatus($status);
144
+        }, $this->mapper->findAllRecent($limit, $offset));
145
+    }
146
+
147
+    /**
148
+     * @param string $userId
149
+     * @return UserStatus
150
+     * @throws DoesNotExistException
151
+     */
152
+    public function findByUserId(string $userId):UserStatus {
153
+        return $this->processStatus($this->mapper->findByUserId($userId));
154
+    }
155
+
156
+    /**
157
+     * @param array $userIds
158
+     * @return UserStatus[]
159
+     */
160
+    public function findByUserIds(array $userIds):array {
161
+        return array_map(function ($status) {
162
+            return $this->processStatus($status);
163
+        }, $this->mapper->findByUserIds($userIds));
164
+    }
165
+
166
+    /**
167
+     * @param string $userId
168
+     * @param string $status
169
+     * @param int|null $statusTimestamp
170
+     * @param bool $isUserDefined
171
+     * @return UserStatus
172
+     * @throws InvalidStatusTypeException
173
+     */
174
+    public function setStatus(string $userId,
175
+                                string $status,
176
+                              ?int $statusTimestamp,
177
+                                bool $isUserDefined): UserStatus {
178
+        try {
179
+            $userStatus = $this->mapper->findByUserId($userId);
180
+        } catch (DoesNotExistException $ex) {
181
+            $userStatus = new UserStatus();
182
+            $userStatus->setUserId($userId);
183
+        }
184
+
185
+        // Check if status-type is valid
186
+        if (!\in_array($status, self::PRIORITY_ORDERED_STATUSES, true)) {
187
+            throw new InvalidStatusTypeException('Status-type "' . $status . '" is not supported');
188
+        }
189
+        if ($statusTimestamp === null) {
190
+            $statusTimestamp = $this->timeFactory->getTime();
191
+        }
192
+
193
+        $userStatus->setStatus($status);
194
+        $userStatus->setStatusTimestamp($statusTimestamp);
195
+        $userStatus->setIsUserDefined($isUserDefined);
196
+        $userStatus->setIsBackup(false);
197
+
198
+        if ($userStatus->getId() === null) {
199
+            return $this->mapper->insert($userStatus);
200
+        }
201
+
202
+        return $this->mapper->update($userStatus);
203
+    }
204
+
205
+    /**
206
+     * @param string $userId
207
+     * @param string $messageId
208
+     * @param int|null $clearAt
209
+     * @return UserStatus
210
+     * @throws InvalidMessageIdException
211
+     * @throws InvalidClearAtException
212
+     */
213
+    public function setPredefinedMessage(string $userId,
214
+                                            string $messageId,
215
+                                         ?int $clearAt): UserStatus {
216
+        try {
217
+            $userStatus = $this->mapper->findByUserId($userId);
218
+        } catch (DoesNotExistException $ex) {
219
+            $userStatus = new UserStatus();
220
+            $userStatus->setUserId($userId);
221
+            $userStatus->setStatus(IUserStatus::OFFLINE);
222
+            $userStatus->setStatusTimestamp(0);
223
+            $userStatus->setIsUserDefined(false);
224
+            $userStatus->setIsBackup(false);
225
+        }
226
+
227
+        if (!$this->predefinedStatusService->isValidId($messageId)) {
228
+            throw new InvalidMessageIdException('Message-Id "' . $messageId . '" is not supported');
229
+        }
230
+
231
+        // Check that clearAt is in the future
232
+        if ($clearAt !== null && $clearAt < $this->timeFactory->getTime()) {
233
+            throw new InvalidClearAtException('ClearAt is in the past');
234
+        }
235
+
236
+        $userStatus->setMessageId($messageId);
237
+        $userStatus->setCustomIcon(null);
238
+        $userStatus->setCustomMessage(null);
239
+        $userStatus->setClearAt($clearAt);
240
+
241
+        if ($userStatus->getId() === null) {
242
+            return $this->mapper->insert($userStatus);
243
+        }
244
+
245
+        return $this->mapper->update($userStatus);
246
+    }
247
+
248
+    /**
249
+     * @param string $userId
250
+     * @param string $status
251
+     * @param string $messageId
252
+     * @param bool $createBackup
253
+     * @throws InvalidStatusTypeException
254
+     * @throws InvalidMessageIdException
255
+     */
256
+    public function setUserStatus(string $userId,
257
+                                            string $status,
258
+                                            string $messageId,
259
+                                            bool $createBackup): void {
260
+        // Check if status-type is valid
261
+        if (!\in_array($status, self::PRIORITY_ORDERED_STATUSES, true)) {
262
+            throw new InvalidStatusTypeException('Status-type "' . $status . '" is not supported');
263
+        }
264
+
265
+        if (!$this->predefinedStatusService->isValidId($messageId)) {
266
+            throw new InvalidMessageIdException('Message-Id "' . $messageId . '" is not supported');
267
+        }
268
+
269
+        if ($createBackup) {
270
+            if ($this->backupCurrentStatus($userId) === false) {
271
+                return; // Already a status set automatically => abort.
272
+            }
273
+
274
+            // If we just created the backup
275
+            $userStatus = new UserStatus();
276
+            $userStatus->setUserId($userId);
277
+        } else {
278
+            try {
279
+                $userStatus = $this->mapper->findByUserId($userId);
280
+            } catch (DoesNotExistException $ex) {
281
+                $userStatus = new UserStatus();
282
+                $userStatus->setUserId($userId);
283
+            }
284
+        }
285
+
286
+        $userStatus->setStatus($status);
287
+        $userStatus->setStatusTimestamp($this->timeFactory->getTime());
288
+        $userStatus->setIsUserDefined(true);
289
+        $userStatus->setIsBackup(false);
290
+        $userStatus->setMessageId($messageId);
291
+        $userStatus->setCustomIcon(null);
292
+        $userStatus->setCustomMessage(null);
293
+        $userStatus->setClearAt(null);
294
+
295
+        if ($userStatus->getId() !== null) {
296
+            $this->mapper->update($userStatus);
297
+            return;
298
+        }
299
+        $this->mapper->insert($userStatus);
300
+    }
301
+
302
+    /**
303
+     * @param string $userId
304
+     * @param string|null $statusIcon
305
+     * @param string|null $message
306
+     * @param int|null $clearAt
307
+     * @return UserStatus
308
+     * @throws InvalidClearAtException
309
+     * @throws InvalidStatusIconException
310
+     * @throws StatusMessageTooLongException
311
+     */
312
+    public function setCustomMessage(string $userId,
313
+                                     ?string $statusIcon,
314
+                                     ?string $message,
315
+                                     ?int $clearAt): UserStatus {
316
+        try {
317
+            $userStatus = $this->mapper->findByUserId($userId);
318
+        } catch (DoesNotExistException $ex) {
319
+            $userStatus = new UserStatus();
320
+            $userStatus->setUserId($userId);
321
+            $userStatus->setStatus(IUserStatus::OFFLINE);
322
+            $userStatus->setStatusTimestamp(0);
323
+            $userStatus->setIsUserDefined(false);
324
+        }
325
+
326
+        // Check if statusIcon contains only one character
327
+        if ($statusIcon !== null && !$this->emojiHelper->isValidSingleEmoji($statusIcon)) {
328
+            throw new InvalidStatusIconException('Status-Icon is longer than one character');
329
+        }
330
+        // Check for maximum length of custom message
331
+        if ($message !== null && \mb_strlen($message) > self::MAXIMUM_MESSAGE_LENGTH) {
332
+            throw new StatusMessageTooLongException('Message is longer than supported length of ' . self::MAXIMUM_MESSAGE_LENGTH . ' characters');
333
+        }
334
+        // Check that clearAt is in the future
335
+        if ($clearAt !== null && $clearAt < $this->timeFactory->getTime()) {
336
+            throw new InvalidClearAtException('ClearAt is in the past');
337
+        }
338
+
339
+        $userStatus->setMessageId(null);
340
+        $userStatus->setCustomIcon($statusIcon);
341
+        $userStatus->setCustomMessage($message);
342
+        $userStatus->setClearAt($clearAt);
343
+
344
+        if ($userStatus->getId() === null) {
345
+            return $this->mapper->insert($userStatus);
346
+        }
347
+
348
+        return $this->mapper->update($userStatus);
349
+    }
350
+
351
+    /**
352
+     * @param string $userId
353
+     * @return bool
354
+     */
355
+    public function clearStatus(string $userId): bool {
356
+        try {
357
+            $userStatus = $this->mapper->findByUserId($userId);
358
+        } catch (DoesNotExistException $ex) {
359
+            // if there is no status to remove, just return
360
+            return false;
361
+        }
362
+
363
+        $userStatus->setStatus(IUserStatus::OFFLINE);
364
+        $userStatus->setStatusTimestamp(0);
365
+        $userStatus->setIsUserDefined(false);
366
+
367
+        $this->mapper->update($userStatus);
368
+        return true;
369
+    }
370
+
371
+    /**
372
+     * @param string $userId
373
+     * @return bool
374
+     */
375
+    public function clearMessage(string $userId): bool {
376
+        try {
377
+            $userStatus = $this->mapper->findByUserId($userId);
378
+        } catch (DoesNotExistException $ex) {
379
+            // if there is no status to remove, just return
380
+            return false;
381
+        }
382
+
383
+        $userStatus->setMessageId(null);
384
+        $userStatus->setCustomMessage(null);
385
+        $userStatus->setCustomIcon(null);
386
+        $userStatus->setClearAt(null);
387
+
388
+        $this->mapper->update($userStatus);
389
+        return true;
390
+    }
391
+
392
+    /**
393
+     * @param string $userId
394
+     * @return bool
395
+     */
396
+    public function removeUserStatus(string $userId): bool {
397
+        try {
398
+            $userStatus = $this->mapper->findByUserId($userId, false);
399
+        } catch (DoesNotExistException $ex) {
400
+            // if there is no status to remove, just return
401
+            return false;
402
+        }
403
+
404
+        $this->mapper->delete($userStatus);
405
+        return true;
406
+    }
407
+
408
+    public function removeBackupUserStatus(string $userId): bool {
409
+        try {
410
+            $userStatus = $this->mapper->findByUserId($userId, true);
411
+        } catch (DoesNotExistException $ex) {
412
+            // if there is no status to remove, just return
413
+            return false;
414
+        }
415
+
416
+        $this->mapper->delete($userStatus);
417
+        return true;
418
+    }
419
+
420
+    /**
421
+     * Processes a status to check if custom message is still
422
+     * up to date and provides translated default status if needed
423
+     *
424
+     * @param UserStatus $status
425
+     * @return UserStatus
426
+     */
427
+    private function processStatus(UserStatus $status): UserStatus {
428
+        $clearAt = $status->getClearAt();
429
+
430
+        if ($status->getStatusTimestamp() < $this->timeFactory->getTime() - self::INVALIDATE_STATUS_THRESHOLD
431
+            && (!$status->getIsUserDefined() || $status->getStatus() === IUserStatus::ONLINE)) {
432
+            $this->cleanStatus($status);
433
+        }
434
+        if ($clearAt !== null && $clearAt < $this->timeFactory->getTime()) {
435
+            $this->cleanStatus($status);
436
+            $this->cleanStatusMessage($status);
437
+        }
438
+        if ($status->getMessageId() !== null) {
439
+            $this->addDefaultMessage($status);
440
+        }
441
+
442
+        return $status;
443
+    }
444
+
445
+    /**
446
+     * @param UserStatus $status
447
+     */
448
+    private function cleanStatus(UserStatus $status): void {
449
+        if ($status->getStatus() === IUserStatus::OFFLINE && !$status->getIsUserDefined()) {
450
+            return;
451
+        }
452
+
453
+        $status->setStatus(IUserStatus::OFFLINE);
454
+        $status->setStatusTimestamp($this->timeFactory->getTime());
455
+        $status->setIsUserDefined(false);
456
+
457
+        $this->mapper->update($status);
458
+    }
459
+
460
+    /**
461
+     * @param UserStatus $status
462
+     */
463
+    private function cleanStatusMessage(UserStatus $status): void {
464
+        $status->setMessageId(null);
465
+        $status->setCustomIcon(null);
466
+        $status->setCustomMessage(null);
467
+        $status->setClearAt(null);
468
+
469
+        $this->mapper->update($status);
470
+    }
471
+
472
+    /**
473
+     * @param UserStatus $status
474
+     */
475
+    private function addDefaultMessage(UserStatus $status): void {
476
+        // If the message is predefined, insert the translated message and icon
477
+        $predefinedMessage = $this->predefinedStatusService->getDefaultStatusById($status->getMessageId());
478
+        if ($predefinedMessage !== null) {
479
+            $status->setCustomMessage($predefinedMessage['message']);
480
+            $status->setCustomIcon($predefinedMessage['icon']);
481
+        }
482
+    }
483
+
484
+    /**
485
+     * @return bool false if there is already a backup. In this case abort the procedure.
486
+     */
487
+    public function backupCurrentStatus(string $userId): bool {
488
+        try {
489
+            $this->mapper->createBackupStatus($userId);
490
+            return true;
491
+        } catch (Exception $ex) {
492
+            if ($ex->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
493
+                return false;
494
+            }
495
+            throw $ex;
496
+        }
497
+    }
498
+
499
+    public function revertUserStatus(string $userId, string $messageId, bool $revertedManually = false): ?UserStatus {
500
+        try {
501
+            /** @var UserStatus $userStatus */
502
+            $backupUserStatus = $this->mapper->findByUserId($userId, true);
503
+        } catch (DoesNotExistException $ex) {
504
+            // No user status to revert, do nothing
505
+            return null;
506
+        }
507
+
508
+        $deleted = $this->mapper->deleteCurrentStatusToRestoreBackup($userId, $messageId);
509
+        if (!$deleted) {
510
+            // Another status is set automatically or no status, do nothing
511
+            return null;
512
+        }
513
+
514
+        if ($revertedManually && $backupUserStatus->getStatus() === IUserStatus::OFFLINE) {
515
+            // When the user reverts the status manually they are online
516
+            $backupUserStatus->setStatus(IUserStatus::ONLINE);
517
+        }
518
+
519
+        $backupUserStatus->setIsBackup(false);
520
+        // Remove the underscore prefix added when creating the backup
521
+        $backupUserStatus->setUserId(substr($backupUserStatus->getUserId(), 1));
522
+        $this->mapper->update($backupUserStatus);
523
+
524
+        return $backupUserStatus;
525
+    }
526
+
527
+    public function revertMultipleUserStatus(array $userIds, string $messageId): void {
528
+        // Get all user statuses and the backups
529
+        $findById = $userIds;
530
+        foreach ($userIds as $userId) {
531
+            $findById[] = '_' . $userId;
532
+        }
533
+        $userStatuses = $this->mapper->findByUserIds($findById);
534
+
535
+        $backups = $restoreIds = $statuesToDelete = [];
536
+        foreach ($userStatuses as $userStatus) {
537
+            if (!$userStatus->getIsBackup()
538
+                && $userStatus->getMessageId() === $messageId) {
539
+                $statuesToDelete[$userStatus->getUserId()] = $userStatus->getId();
540
+            } else if ($userStatus->getIsBackup()) {
541
+                $backups[$userStatus->getUserId()] = $userStatus->getId();
542
+            }
543
+        }
544
+
545
+        // For users with both (normal and backup) delete the status when matching
546
+        foreach ($statuesToDelete as $userId => $statusId) {
547
+            $backupUserId = '_' . $userId;
548
+            if (isset($backups[$backupUserId])) {
549
+                $restoreIds[] = $backups[$backupUserId];
550
+            }
551
+        }
552
+
553
+        $this->mapper->deleteByIds(array_values($statuesToDelete));
554
+
555
+        // For users that matched restore the previous status
556
+        $this->mapper->restoreBackupStatuses($restoreIds);
557
+    }
558 558
 }
Please login to merge, or discard this patch.
apps/user_status/lib/Capabilities.php 1 patch
Indentation   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -34,22 +34,22 @@
 block discarded – undo
34 34
  * @package OCA\UserStatus
35 35
  */
36 36
 class Capabilities implements ICapability {
37
-	private IEmojiHelper $emojiHelper;
37
+    private IEmojiHelper $emojiHelper;
38 38
 
39
-	public function __construct(IEmojiHelper $emojiHelper) {
40
-		$this->emojiHelper = $emojiHelper;
41
-	}
39
+    public function __construct(IEmojiHelper $emojiHelper) {
40
+        $this->emojiHelper = $emojiHelper;
41
+    }
42 42
 
43
-	/**
44
-	 * @inheritDoc
45
-	 */
46
-	public function getCapabilities() {
47
-		return [
48
-			'user_status' => [
49
-				'enabled' => true,
50
-				'restore' => true,
51
-				'supports_emoji' => $this->emojiHelper->doesPlatformSupportEmoji(),
52
-			],
53
-		];
54
-	}
43
+    /**
44
+     * @inheritDoc
45
+     */
46
+    public function getCapabilities() {
47
+        return [
48
+            'user_status' => [
49
+                'enabled' => true,
50
+                'restore' => true,
51
+                'supports_emoji' => $this->emojiHelper->doesPlatformSupportEmoji(),
52
+            ],
53
+        ];
54
+    }
55 55
 }
Please login to merge, or discard this patch.