diff --git a/scripts/langindex.json b/scripts/langindex.json
index 70e2ef078..ad31aebcd 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -2332,6 +2332,7 @@
"core.userdeleted": "moodle",
"core.userdetails": "moodle",
"core.usernotfullysetup": "error",
+ "core.usersuspended": "moodle",
"core.users": "moodle",
"core.view": "moodle",
"core.viewcode": "local_moodlemobileapp",
diff --git a/src/core/classes/site.ts b/src/core/classes/site.ts
index 8989abfde..dc702baf9 100644
--- a/src/core/classes/site.ts
+++ b/src/core/classes/site.ts
@@ -610,11 +610,17 @@ export class CoreSite {
CoreEvents.trigger(CoreEvents.SESSION_EXPIRED, {}, this.id);
// Change error message. Try to get data from cache, the event will handle the error.
error.message = Translate.instant('core.lostconnection');
- } else if (error.errorcode === 'userdeleted') {
+ } else if (error.errorcode === 'userdeleted' || error.errorcode === 'wsaccessuserdeleted') {
// User deleted, trigger event.
CoreEvents.trigger(CoreEvents.USER_DELETED, { params: data }, this.id);
error.message = Translate.instant('core.userdeleted');
+ throw new CoreWSError(error);
+ } else if (error.errorcode === 'wsaccessusersuspended') {
+ // User suspended, trigger event.
+ CoreEvents.trigger(CoreEvents.USER_SUSPENDED, { params: data }, this.id);
+ error.message = Translate.instant('core.usersuspended');
+
throw new CoreWSError(error);
} else if (error.errorcode === 'forcepasswordchangenotice') {
// Password Change Forced, trigger event. Try to get data from cache, the event will handle the error.
diff --git a/src/core/features/user/pages/profile/profile.html b/src/core/features/user/pages/profile/profile.html
index 1d12c3003..e135eced7 100644
--- a/src/core/features/user/pages/profile/profile.html
+++ b/src/core/features/user/pages/profile/profile.html
@@ -88,6 +88,7 @@
+
diff --git a/src/core/features/user/pages/profile/profile.page.ts b/src/core/features/user/pages/profile/profile.page.ts
index e23ff8f3c..ba3061238 100644
--- a/src/core/features/user/pages/profile/profile.page.ts
+++ b/src/core/features/user/pages/profile/profile.page.ts
@@ -53,6 +53,7 @@ export class CoreUserProfilePage implements OnInit, OnDestroy {
isLoadingHandlers = false;
user?: CoreUserProfile;
isDeleted = false;
+ isSuspended = false;
isEnrolled = true;
rolesFormatted?: string;
actionHandlers: CoreUserProfileHandlerData[] = [];
@@ -113,7 +114,8 @@ export class CoreUserProfilePage implements OnInit, OnDestroy {
try {
await CoreUser.logView(this.userId, this.courseId, this.user.fullname);
} catch (error) {
- this.isDeleted = error?.errorcode === 'userdeleted';
+ this.isDeleted = error?.errorcode === 'userdeleted' || error?.errorcode === 'wsaccessuserdeleted';
+ this.isSuspended = error?.errorcode === 'wsaccessusersuspended';
this.isEnrolled = error?.errorcode !== 'notenrolledprofile';
}
} finally {
diff --git a/src/core/features/user/services/user.ts b/src/core/features/user/services/user.ts
index 15e2236ad..c74c9642d 100644
--- a/src/core/features/user/services/user.ts
+++ b/src/core/features/user/services/user.ts
@@ -22,7 +22,7 @@ import { CoreUserOffline } from './user-offline';
import { CoreLogger } from '@singletons/logger';
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
import { makeSingleton, Translate } from '@singletons';
-import { CoreEvents } from '@singletons/events';
+import { CoreEvents, CoreEventSiteData, CoreEventUserDeletedData, CoreEventUserSuspendedData } from '@singletons/events';
import { CoreStatusWithWarningsWSResponse, CoreWSExternalWarning } from '@services/ws';
import { CoreError } from '@classes/errors/error';
import { USERS_TABLE_NAME, CoreUserDBRecord } from './database/user';
@@ -61,24 +61,8 @@ export class CoreUserProvider {
constructor() {
this.logger = CoreLogger.getInstance('CoreUserProvider');
- CoreEvents.on(CoreEvents.USER_DELETED, (data) => {
- // Search for userid in params.
- let userId = 0;
-
- if (data.params.userid) {
- userId = data.params.userid;
- } else if (data.params.userids) {
- userId = data.params.userids[0];
- } else if (data.params.field === 'id' && data.params.values && data.params.values.length) {
- userId = data.params.values[0];
- } else if (data.params.userlist && data.params.userlist.length) {
- userId = data.params.userlist[0].userid;
- }
-
- if (userId > 0) {
- this.deleteStoredUser(userId, data.siteId);
- }
- });
+ CoreEvents.on(CoreEvents.USER_DELETED, data => this.handleUserKickedOutEvent(data));
+ CoreEvents.on(CoreEvents.USER_SUSPENDED, data => this.handleUserKickedOutEvent(data));
}
/**
@@ -153,6 +137,32 @@ export class CoreUserProvider {
return result.profileimageurl!;
}
+ /**
+ * Handle an event where a user was kicked out of the site.
+ *
+ * @param data Event data.
+ */
+ async handleUserKickedOutEvent(
+ data: CoreEventSiteData & (CoreEventUserDeletedData | CoreEventUserSuspendedData),
+ ): Promise {
+ // Search for userid in params.
+ let userId = 0;
+
+ if (data.params.userid) {
+ userId = data.params.userid;
+ } else if (data.params.userids) {
+ userId = data.params.userids[0];
+ } else if (data.params.field === 'id' && data.params.values && data.params.values.length) {
+ userId = data.params.values[0];
+ } else if (data.params.userlist && data.params.userlist.length) {
+ userId = data.params.userlist[0].userid;
+ }
+
+ if (userId > 0) {
+ await this.deleteStoredUser(userId, data.siteId);
+ }
+ }
+
/**
* Store user basic information in local DB to be retrieved if the WS call fails.
*
diff --git a/src/core/lang.json b/src/core/lang.json
index 624038e0b..e20334ff9 100644
--- a/src/core/lang.json
+++ b/src/core/lang.json
@@ -327,6 +327,7 @@
"userdeleted": "This user account has been deleted",
"userdetails": "User details",
"usernotfullysetup": "User not fully set-up",
+ "usersuspended": "This user account has been suspended",
"users": "Users",
"view": "View",
"viewcode": "View code",
diff --git a/src/core/services/utils/utils.ts b/src/core/services/utils/utils.ts
index 2b72fb6c3..b928c0e5e 100644
--- a/src/core/services/utils/utils.ts
+++ b/src/core/services/utils/utils.ts
@@ -862,6 +862,7 @@ export class CoreUtilsProvider {
error.errorcode != 'userdeleted' && error.errorcode != 'upgraderunning' &&
error.errorcode != 'forcepasswordchangenotice' && error.errorcode != 'usernotfullysetup' &&
error.errorcode != 'sitepolicynotagreed' && error.errorcode != 'sitemaintenance' &&
+ error.errorcode != 'wsaccessusersuspended' && error.errorcode != 'wsaccessuserdeleted' &&
!this.isExpiredTokenError(error)));
}
diff --git a/src/core/singletons/events.ts b/src/core/singletons/events.ts
index 288897bbd..683b4d5af 100644
--- a/src/core/singletons/events.ts
+++ b/src/core/singletons/events.ts
@@ -44,6 +44,7 @@ export interface CoreEventsData {
[CoreEvents.COURSE_STATUS_CHANGED]: CoreEventCourseStatusChanged;
[CoreEvents.PACKAGE_STATUS_CHANGED]: CoreEventPackageStatusChanged;
[CoreEvents.USER_DELETED]: CoreEventUserDeletedData;
+ [CoreEvents.USER_SUSPENDED]: CoreEventUserSuspendedData;
[CoreEvents.FORM_ACTION]: CoreEventFormActionData;
[CoreEvents.NOTIFICATION_SOUND_CHANGED]: CoreEventNotificationSoundChangedData;
[CoreEvents.SELECT_COURSE_TAB]: CoreEventSelectCourseTabData;
@@ -85,6 +86,7 @@ export class CoreEvents {
static readonly MANUAL_COMPLETION_CHANGED = 'manual_completion_changed';
static readonly COMPLETION_CHANGED = 'completion_changed';
static readonly USER_DELETED = 'user_deleted';
+ static readonly USER_SUSPENDED = 'user_suspended';
static readonly PACKAGE_STATUS_CHANGED = 'package_status_changed';
static readonly COURSE_STATUS_CHANGED = 'course_status_changed';
static readonly SECTION_STATUS_CHANGED = 'section_status_changed';
@@ -308,6 +310,14 @@ export type CoreEventUserDeletedData = {
params: any; // Params sent to the WS that failed.
};
+/**
+ * Data passed to USER_SUSPENDED event.
+ */
+export type CoreEventUserSuspendedData = {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ params: any; // Params sent to the WS that failed.
+};
+
export enum CoreEventFormAction {
CANCEL = 'cancel',
SUBMIT = 'submit',