diff --git a/scripts/langindex.json b/scripts/langindex.json
index 308ec401c..4f883c941 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -522,7 +522,7 @@
   "addon.mod_choice.savemychoice": "choice",
   "addon.mod_choice.userchoosethisoption": "choice",
   "addon.mod_choice.yourselection": "choice",
-  "addon.mod_data.actions": "data",
+  "addon.mod_data.actionsmenu": "data",
   "addon.mod_data.addentries": "data",
   "addon.mod_data.advancedsearch": "data",
   "addon.mod_data.alttext": "data",
@@ -2200,6 +2200,7 @@
   "core.nopermissionerror": "local_moodlemobileapp",
   "core.nopermissions": "error",
   "core.nopermissiontoaccesspage": "error",
+  "core.noreplyname": "moodle",
   "core.noresults": "moodle",
   "core.noselection": "form",
   "core.notapplicable": "local_moodlemobileapp",
diff --git a/src/addons/mod/data/components/action/addon-mod-data-action.html b/src/addons/mod/data/components/action/addon-mod-data-action.html
index cf233c3fa..aa4160d05 100644
--- a/src/addons/mod/data/components/action/addon-mod-data-action.html
+++ b/src/addons/mod/data/components/action/addon-mod-data-action.html
@@ -1,5 +1,5 @@
 <ion-button size="small" *ngIf="action == 'actionsmenu'" fill="clear" (click)="actionsMenu()"
-    [attr.aria-label]="'addon.mod_data.actions' | translate">
+    [attr.aria-label]="'addon.mod_data.actionsmenu' | translate">
     <ion-icon name="fas-ellipsis-vertical" slot="icon-only" aria-hidden="true"></ion-icon>
 </ion-button>
 
diff --git a/src/addons/mod/data/lang.json b/src/addons/mod/data/lang.json
index e77e2fcd1..d15f18e32 100644
--- a/src/addons/mod/data/lang.json
+++ b/src/addons/mod/data/lang.json
@@ -1,5 +1,5 @@
 {
-    "actions": "Actions menu",
+    "actionsmenu": "Actions menu",
     "addentries": "Add entries",
     "advancedsearch": "Advanced search",
     "alttext": "Alternative text",
diff --git a/src/addons/notifications/classes/legacy-notifications-source.ts b/src/addons/notifications/classes/legacy-notifications-source.ts
index 564689350..22319ca5f 100644
--- a/src/addons/notifications/classes/legacy-notifications-source.ts
+++ b/src/addons/notifications/classes/legacy-notifications-source.ts
@@ -13,8 +13,10 @@
 // limitations under the License.
 
 import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
-import { AddonNotificationsGetReadType } from '@addons/notifications/services/notifications';
-import { AddonNotificationsNotificationToRender } from '@addons/notifications/services/notifications-helper';
+import {
+    AddonNotificationsGetReadType,
+    AddonNotificationsNotificationMessageFormatted,
+} from '@addons/notifications/services/notifications';
 
 /**
  * Provides a list of notifications using legacy web services.
@@ -25,10 +27,10 @@ export class AddonLegacyNotificationsNotificationsSource extends AddonNotificati
      * @inheritdoc
      */
     protected async loadPageItems(page: number): Promise<{
-        items: AddonNotificationsNotificationToRender[];
+        items: AddonNotificationsNotificationMessageFormatted[];
         hasMoreItems: boolean;
     }> {
-        let items: AddonNotificationsNotificationToRender[] = [];
+        let items: AddonNotificationsNotificationMessageFormatted[] = [];
         let hasMoreItems = false;
         let pageUnreadCount = 0;
         const pageLength = this.getPageLength();
diff --git a/src/addons/notifications/classes/notifications-source.ts b/src/addons/notifications/classes/notifications-source.ts
index 4ff8edb85..033201956 100644
--- a/src/addons/notifications/classes/notifications-source.ts
+++ b/src/addons/notifications/classes/notifications-source.ts
@@ -15,22 +15,23 @@
 import {
     AddonNotifications,
     AddonNotificationsGetReadType,
+    AddonNotificationsNotificationMessageFormatted,
     AddonNotificationsProvider,
 } from '@addons/notifications/services/notifications';
-import { AddonNotificationsNotificationToRender } from '@addons/notifications/services/notifications-helper';
 import { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source';
 
 /**
  * Provides a list of notifications.
  */
-export class AddonNotificationsNotificationsSource extends CoreRoutedItemsManagerSource<AddonNotificationsNotificationToRender> {
+export class AddonNotificationsNotificationsSource
+    extends CoreRoutedItemsManagerSource<AddonNotificationsNotificationMessageFormatted> {
 
     protected totals: Record<string, number> = {};
 
     /**
      * @inheritdoc
      */
-    getItemPath(notification: AddonNotificationsNotificationToRender): string {
+    getItemPath(notification: AddonNotificationsNotificationMessageFormatted): string {
         return notification.id.toString();
     }
 
@@ -47,7 +48,7 @@ export class AddonNotificationsNotificationsSource extends CoreRoutedItemsManage
      * @inheritdoc
      */
     protected async loadPageItems(page: number): Promise<{
-        items: AddonNotificationsNotificationToRender[];
+        items: AddonNotificationsNotificationMessageFormatted[];
         hasMoreItems: boolean;
     }> {
         const results = await this.loadNotifications(AddonNotificationsGetReadType.BOTH, page * this.getPageLength());
@@ -67,7 +68,7 @@ export class AddonNotificationsNotificationsSource extends CoreRoutedItemsManage
      * @returns Notifications and whether there are any more.
      */
     protected async loadNotifications(type: AddonNotificationsGetReadType, offset: number, limit?: number): Promise<{
-        notifications: AddonNotificationsNotificationToRender[];
+        notifications: AddonNotificationsNotificationMessageFormatted[];
         hasMoreNotifications: boolean;
     }> {
         limit = limit ?? this.getPageLength();
@@ -94,7 +95,7 @@ export class AddonNotificationsNotificationsSource extends CoreRoutedItemsManage
     /**
      * @inheritdoc
      */
-    protected setItems(notifications: AddonNotificationsNotificationToRender[], hasMoreItems: boolean): void {
+    protected setItems(notifications: AddonNotificationsNotificationMessageFormatted[], hasMoreItems: boolean): void {
         const sortedNotifications = notifications.slice(0);
 
         sortedNotifications.sort((a, b) => a.timecreated < b.timecreated ? 1 : -1);
diff --git a/src/addons/notifications/notifications.scss b/src/addons/notifications/notifications.scss
index 0583e27fa..2f144dd2a 100644
--- a/src/addons/notifications/notifications.scss
+++ b/src/addons/notifications/notifications.scss
@@ -34,6 +34,9 @@
             width: var(--icon-size);
             height: var(--icon-size);
         }
+        ion-icon {
+            font-size: var(--icon-size);
+        }
         padding: 0.7rem;
         background: var(--background-color);
         border-radius: var(--small-radius);
@@ -43,4 +46,11 @@
         --module-icon-size: var(--icon-size);
         @include margin(6px, 8px, 6px, 0px);
     }
+
+    .core-notification-img {
+        @include margin(6px, 8px, 6px, 0px);
+        width: var(--core-avatar-size);
+        height: var(--core-avatar-size);
+        object-fit: cover;
+    }
 }
diff --git a/src/addons/notifications/pages/list/list.html b/src/addons/notifications/pages/list/list.html
index c0ccfd14d..b7f7a5897 100644
--- a/src/addons/notifications/pages/list/list.html
+++ b/src/addons/notifications/pages/list/list.html
@@ -28,21 +28,18 @@
                 <core-user-avatar *ngIf="notification.useridfrom > 0" [user]="notification" slot="start"
                     [profileUrl]="notification.profileimageurlfrom" [fullname]="notification.userfromfullname"
                     [userId]="notification.useridfrom">
-                    <div class="core-avatar-extra-img" *ngIf="notification.iconurl && !notification.modname">
+                    <div class="core-avatar-extra-img" *ngIf="notification.iconurl">
                         <img [src]="notification.iconurl" alt="" role="presentation">
                     </div>
-                    <core-mod-icon *ngIf="notification.modname" [modicon]="notification.iconurl" [modname]="notification.modname"
-                        [showAlt]="false">
-                    </core-mod-icon>
                 </core-user-avatar>
 
-                <ng-container *ngIf="notification.useridfrom <= 0 && notification.iconurl">
-                    <div class="core-notification-icon" *ngIf="!notification.modname" slot="start">
-                        <img [src]="notification.iconurl" alt="" role="presentation">
+                <ng-container *ngIf="notification.useridfrom <= 0">
+                    <img *ngIf="notification.imgUrl" class="core-notification-img" [src]="notification.imgUrl" core-external-content alt=""
+                        role="presentation" slot="start">
+                    <div class="core-notification-icon" *ngIf="!notification.imgUrl" slot="start">
+                        <img *ngIf="notification.iconurl" [src]="notification.iconurl" core-external-content alt="" role="presentation">
+                        <ion-icon *ngIf="!notification.iconurl" name="fas-bell" aria-hidden="true"></ion-icon>
                     </div>
-                    <core-mod-icon *ngIf="notification.modname" [modicon]="notification.iconurl" [modname]="notification.modname"
-                        [showAlt]="false" class="core-notification-icon" slot="start">
-                    </core-mod-icon>
                 </ng-container>
 
                 <ion-label>
diff --git a/src/addons/notifications/pages/list/list.ts b/src/addons/notifications/pages/list/list.ts
index e26e0f344..dc935a079 100644
--- a/src/addons/notifications/pages/list/list.ts
+++ b/src/addons/notifications/pages/list/list.ts
@@ -20,7 +20,7 @@ import { CoreDomUtils } from '@services/utils/dom';
 import { CoreUtils } from '@services/utils/utils';
 import { CoreEventObserver, CoreEvents } from '@singletons/events';
 import {
-    AddonNotifications, AddonNotificationsProvider,
+    AddonNotifications, AddonNotificationsNotificationMessageFormatted, AddonNotificationsProvider,
 } from '../../services/notifications';
 import { CoreNavigator } from '@services/navigator';
 import { CoreSplitViewComponent } from '@components/split-view/split-view';
@@ -31,7 +31,6 @@ import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-lin
 import { CoreTimeUtils } from '@services/utils/time';
 import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
 import { CoreListItemsManager } from '@classes/items-management/list-items-manager';
-import { AddonNotificationsNotificationToRender } from '@addons/notifications/services/notifications-helper';
 import { AddonLegacyNotificationsNotificationsSource } from '@addons/notifications/classes/legacy-notifications-source';
 
 /**
@@ -45,7 +44,7 @@ import { AddonLegacyNotificationsNotificationsSource } from '@addons/notificatio
 export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
 
     @ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent;
-    notifications!: CoreListItemsManager<AddonNotificationsNotificationToRender, AddonNotificationsNotificationsSource>;
+    notifications!: CoreListItemsManager<AddonNotificationsNotificationMessageFormatted, AddonNotificationsNotificationsSource>;
     fetchMoreNotificationsFailed = false;
     canMarkAllNotificationsAsRead = false;
     loadingMarkAllNotificationsAsRead = false;
diff --git a/src/addons/notifications/pages/notification/notification.html b/src/addons/notifications/pages/notification/notification.html
index 6c7f95dd6..808cfe861 100644
--- a/src/addons/notifications/pages/notification/notification.html
+++ b/src/addons/notifications/pages/notification/notification.html
@@ -10,40 +10,40 @@
 </ion-header>
 <ion-content [core-swipe-navigation]="notifications">
     <core-loading [hideUntil]="loaded">
-        <div class="list-item-limited-width">
+        <div class="list-item-limited-width" *ngIf="notification">
 
             <ion-item class="ion-text-wrap core-notification-title" lines="full">
-                <core-user-avatar *ngIf="userIdFrom > 0" slot="start" [userId]="userIdFrom" [profileUrl]="profileImageUrlFrom"
-                    [fullname]="userFromFullName">
-                    <div class="core-avatar-extra-img" *ngIf="iconUrl && !modname">
-                        <img [src]="iconUrl" alt="" role="presentation">
+                <core-user-avatar *ngIf="notification.useridfrom > 0" slot="start" [userId]="notification.useridfrom"
+                    [profileUrl]="notification.profileimageurlfrom" [fullname]="notification.userfromfullname">
+                    <div class="core-avatar-extra-img" *ngIf="notification.iconurl">
+                        <img [src]="notification.iconurl" alt="" role="presentation">
                     </div>
-                    <core-mod-icon *ngIf="modname" [modicon]="iconUrl" [modname]="modname" [showAlt]="false">
-                    </core-mod-icon>
                 </core-user-avatar>
 
-                <ng-container *ngIf="userIdFrom <= 0 && iconUrl">
-                    <div class="core-notification-icon" *ngIf="!modname" slot="start">
-                        <img [src]="iconUrl" alt="" role="presentation">
+                <ng-container *ngIf="notification.useridfrom <= 0">
+                    <img *ngIf="notification.imgUrl" class="core-notification-img" [src]="notification.imgUrl" core-external-content alt=""
+                        role="presentation" slot="start">
+                    <div class="core-notification-icon" *ngIf="!notification.imgUrl" slot="start">
+                        <img *ngIf="notification.iconurl" [src]="notification.iconurl" core-external-content alt="" role="presentation">
+                        <ion-icon *ngIf="!notification.iconurl" name="fas-bell" aria-hidden="true"></ion-icon>
                     </div>
-                    <core-mod-icon *ngIf="modname" [modicon]="iconUrl" [modname]="modname" [showAlt]="false" class="core-notification-icon"
-                        slot="start">
-                    </core-mod-icon>
                 </ng-container>
 
                 <ion-label>
                     <p class="item-heading">
-                        <core-format-text [text]="subject" contextLevel="system" [contextInstanceId]="0" [wsNotFiltered]="true">
+                        <core-format-text [text]="notification.subject" contextLevel="system" [contextInstanceId]="0"
+                            [wsNotFiltered]="true">
                         </core-format-text>
                     </p>
-                    <p *ngIf="timecreated > 0">{{ timecreated | coreTimeAgo }}<ng-container *ngIf="userIdFrom > 0"> · {{
-                            userFromFullName }}</ng-container>
+                    <p *ngIf="notification.timecreated > 0">
+                        {{ notification.timecreated | coreTimeAgo }}
+                        <ng-container *ngIf="notification.useridfrom > 0"> · {{ notification.userfromfullname }}</ng-container>
                     </p>
                 </ion-label>
             </ion-item>
             <ion-item class="ion-text-wrap core-notification-body">
                 <ion-label>
-                    <core-format-text [text]="content | coreCreateLinks" contextLevel="system" [contextInstanceId]="0">
+                    <core-format-text [text]="notification.mobiletext | coreCreateLinks" contextLevel="system" [contextInstanceId]="0">
                     </core-format-text>
                 </ion-label>
             </ion-item>
diff --git a/src/addons/notifications/pages/notification/notification.ts b/src/addons/notifications/pages/notification/notification.ts
index eea8ff181..7a5b4edb8 100644
--- a/src/addons/notifications/pages/notification/notification.ts
+++ b/src/addons/notifications/pages/notification/notification.ts
@@ -14,10 +14,10 @@
 
 import { AddonLegacyNotificationsNotificationsSource } from '@addons/notifications/classes/legacy-notifications-source';
 import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
-import { AddonNotificationsNotificationData } from '@addons/notifications/services/handlers/push-click';
+import { AddonNotificationsPushNotification } from '@addons/notifications/services/handlers/push-click';
+import { AddonNotifications, AddonNotificationsNotificationMessageFormatted } from '@addons/notifications/services/notifications';
 import {
     AddonNotificationsHelper,
-    AddonNotificationsNotificationToRender,
 } from '@addons/notifications/services/notifications-helper';
 import { Component, OnDestroy, OnInit } from '@angular/core';
 import { ActivatedRouteSnapshot } from '@angular/router';
@@ -39,21 +39,15 @@ import { CoreDomUtils } from '@services/utils/dom';
 export class AddonNotificationsNotificationPage implements OnInit, OnDestroy {
 
     notifications?: AddonNotificationSwipeItemsManager;
-    subject = ''; // Notification subject.
-    content = ''; // Notification content.
-    userIdFrom = -1; // User ID who sent the notification.
+    notification?: AddonNotificationsNotificationMessageFormatted;
     profileImageUrlFrom?: string; // Avatar of the user who sent the notification.
-    userFromFullName?: string; // Name of the user who sent the notification.
-    iconUrl?: string; // Icon URL.
-    modname?: string; // Module name.
     loaded = false;
-    timecreated = 0;
 
     // Actions data.
     actions: CoreContentLinksAction[] = [];
-    contextUrl?: string;
-    courseId?: number;
-    actionsData?: Record<string, unknown>; // Extra data to handle the URL.
+    protected contextUrl?: string;
+    protected courseId?: number;
+    protected actionsData?: Record<string, string|number>; // Extra data to handle the URL.
 
     /**
      * @inheritdoc
@@ -70,31 +64,11 @@ export class AddonNotificationsNotificationPage implements OnInit, OnDestroy {
             return;
         }
 
-        if ('subject' in notification) {
-            this.subject = notification.subject;
-            this.content = notification.mobiletext || notification.fullmessagehtml;
-            this.userIdFrom = notification.useridfrom;
-            this.profileImageUrlFrom = notification.profileimageurlfrom;
-            this.userFromFullName = notification.userfromfullname;
-            this.iconUrl = notification.iconurl;
-            if (notification.moodlecomponent?.startsWith('mod_') && notification.iconurl) {
-                const modname = notification.moodlecomponent.substring(4);
-                if (notification.iconurl.match('/theme/image.php/[^/]+/' + modname + '/[-0-9]*/') ||
-                        notification.iconurl.match('/theme/image.php/[^/]+/' + notification.moodlecomponent + '/[-0-9]*/')) {
-                    this.modname = modname;
-                }
-            }
-            this.timecreated = notification.timecreated;
-        } else {
-            this.subject = notification.title || '';
-            this.content = notification.message || '';
-            this.userIdFrom = notification.userfromid ? Number(notification.userfromid) : -1;
-            this.profileImageUrlFrom = notification.senderImage;
-            this.userFromFullName = notification.userfromfullname;
-            this.timecreated = Number(notification.date ?? 0);
-        }
+        this.notification = 'subject' in notification ?
+            notification :
+            await AddonNotifications.convertPushToMessage(notification);
 
-        await this.loadActions(notification);
+        await this.loadActions(this.notification);
         AddonNotificationsHelper.markNotificationAsRead(notification);
 
         this.loaded = true;
@@ -153,7 +127,7 @@ export class AddonNotificationsNotificationPage implements OnInit, OnDestroy {
      * @param notification Notification.
      * @returns Promise resolved when done.
      */
-    async loadActions(notification: AddonNotificationsNotification): Promise<void> {
+    async loadActions(notification: AddonNotificationsNotificationMessageFormatted): Promise<void> {
         if (!notification.contexturl && (!notification.customdata || !notification.customdata.appurl)) {
             // No URL, nothing to do.
             return;
@@ -161,7 +135,7 @@ export class AddonNotificationsNotificationPage implements OnInit, OnDestroy {
 
         let actions: CoreContentLinksAction[] = [];
         this.actionsData = notification.customdata;
-        this.contextUrl = notification.contexturl;
+        this.contextUrl = notification.contexturl || undefined;
         this.courseId = 'courseid' in notification ? notification.courseid : undefined;
 
         // Treat appurl first if any.
@@ -231,4 +205,4 @@ class AddonNotificationSwipeItemsManager extends CoreSwipeNavigationItemsManager
 
 }
 
-type AddonNotificationsNotification = AddonNotificationsNotificationToRender | AddonNotificationsNotificationData;
+type AddonNotificationsNotification = AddonNotificationsNotificationMessageFormatted | AddonNotificationsPushNotification;
diff --git a/src/addons/notifications/services/handlers/push-click.ts b/src/addons/notifications/services/handlers/push-click.ts
index 197ac86b8..8354de06a 100644
--- a/src/addons/notifications/services/handlers/push-click.ts
+++ b/src/addons/notifications/services/handlers/push-click.ts
@@ -41,7 +41,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
      * @param notification The notification to check.
      * @returns Whether the notification click is handled by this handler
      */
-    async handles(notification: AddonNotificationsNotificationData): Promise<boolean> {
+    async handles(notification: AddonNotificationsPushNotification): Promise<boolean> {
         if (!notification.moodlecomponent) {
             // The notification doesn't come from Moodle. Handle it.
             return true;
@@ -63,7 +63,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
      * @param notification Notification to mark.
      * @returns Promise resolved when done.
      */
-    protected async markAsRead(notification: AddonNotificationsNotificationData): Promise<void> {
+    protected async markAsRead(notification: AddonNotificationsPushNotification): Promise<void> {
         await CoreUtils.ignoreErrors(AddonNotificationsHelper.markNotificationAsRead(notification));
     }
 
@@ -73,7 +73,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
      * @param notification The notification to check.
      * @returns Promise resolved when done.
      */
-    async handleClick(notification: AddonNotificationsNotificationData): Promise<void> {
+    async handleClick(notification: AddonNotificationsPushNotification): Promise<void> {
 
         if (notification.customdata?.extendedtext) {
             // Display the text in a modal.
@@ -137,7 +137,7 @@ export class AddonNotificationsPushClickHandlerService implements CorePushNotifi
 
 export const AddonNotificationsPushClickHandler = makeSingleton(AddonNotificationsPushClickHandlerService);
 
-export type AddonNotificationsNotificationData = CorePushNotificationsNotificationBasicData & {
+export type AddonNotificationsPushNotification = CorePushNotificationsNotificationBasicData & {
     contexturl?: string; // URL related to the notification.
     savedmessageid?: number; // Notification ID (optional).
     id?: number; // Notification ID (optional).
diff --git a/src/addons/notifications/services/notifications-helper.ts b/src/addons/notifications/services/notifications-helper.ts
index 6dec099d1..e03c2b10c 100644
--- a/src/addons/notifications/services/notifications-helper.ts
+++ b/src/addons/notifications/services/notifications-helper.ts
@@ -28,7 +28,7 @@ import {
     AddonNotificationsProvider,
 } from './notifications';
 import { CoreEvents } from '@singletons/events';
-import { AddonNotificationsNotificationData } from './handlers/push-click';
+import { AddonNotificationsPushNotification } from './handlers/push-click';
 import { CoreTimeUtils } from '@services/utils/time';
 
 /**
@@ -42,23 +42,12 @@ export class AddonNotificationsHelperProvider {
      *
      * @param notification The notification object.
      * @returns The notification formatted to render.
+     * @deprecated since 4.2. This function isn't needed anymore.
      */
     formatNotificationText(
         notification: AddonNotificationsNotificationMessageFormatted,
-    ): AddonNotificationsNotificationToRender {
-        const formattedNotification: AddonNotificationsNotificationToRender = notification;
-
-        if (notification.moodlecomponent?.startsWith('mod_') && notification.iconurl) {
-            const modname = notification.moodlecomponent.substring(4);
-            if (notification.iconurl.match('/theme/image.php/[^/]+/' + modname + '/[-0-9]*/') ||
-                    notification.iconurl.match('/theme/image.php/[^/]+/' + notification.moodlecomponent + '/[-0-9]*/')) {
-                formattedNotification.modname = modname;
-            }
-        } else {
-            formattedNotification.iconurl = formattedNotification.iconurl || undefined; // Make sure the property exists.
-        }
-
-        return formattedNotification;
+    ): AddonNotificationsNotificationMessageFormatted {
+        return notification;
     }
 
     /**
@@ -128,7 +117,7 @@ export class AddonNotificationsHelperProvider {
      * @returns Promise resolved when done.
      */
     async markNotificationAsRead(
-        notification: AddonNotificationsNotificationMessageFormatted | AddonNotificationsNotificationData,
+        notification: AddonNotificationsNotificationMessageFormatted | AddonNotificationsPushNotification,
         siteId?: string,
     ): Promise<boolean> {
         if ('read' in notification && (notification.read || notification.timeread > 0)) {
@@ -197,11 +186,3 @@ type AddonNotificationsPreferencesNotificationProcessorFormatted = AddonNotifica
 export type AddonNotificationsPreferencesProcessorFormatted = AddonNotificationsPreferencesProcessor & {
     supported?: boolean; // Calculated in the app. Whether the processor is supported in the app.
 };
-
-/**
- * Notification with some calculated data to render it.
- */
-export type AddonNotificationsNotificationToRender = AddonNotificationsNotificationMessageFormatted & {
-    iconurl?: string;
-    modname?: string;
-};
diff --git a/src/addons/notifications/services/notifications.ts b/src/addons/notifications/services/notifications.ts
index 3e7e3f715..83ddd8b4d 100644
--- a/src/addons/notifications/services/notifications.ts
+++ b/src/addons/notifications/services/notifications.ts
@@ -18,11 +18,12 @@ import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites';
 import { CoreWSExternalWarning } from '@services/ws';
 import { CoreTextUtils } from '@services/utils/text';
 import { CoreTimeUtils } from '@services/utils/time';
-import { CoreUser } from '@features/user/services/user';
+import { CoreUser, USER_NOREPLY_USER } from '@features/user/services/user';
 import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
 import { CoreLogger } from '@singletons/logger';
-import { makeSingleton } from '@singletons';
+import { Translate, makeSingleton } from '@singletons';
 import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
+import { AddonNotificationsPushNotification } from './handlers/push-click';
 
 declare module '@singletons/events' {
 
@@ -56,6 +57,48 @@ export class AddonNotificationsProvider {
         this.logger = CoreLogger.getInstance('AddonNotificationsProvider');
     }
 
+    /**
+     * Convert a push notification data to use the same format as the get_messages WS.
+     *
+     * @param notification Push notification to convert.
+     * @returns Converted notification.
+     */
+    async convertPushToMessage(
+        notification: AddonNotificationsPushNotification,
+    ): Promise<AddonNotificationsNotificationMessageFormatted> {
+        const message = notification.message ?? '';
+        const siteInfo = CoreSites.getCurrentSite()?.getInfo();
+
+        if (notification.senderImage && notification.customdata && !notification.customdata.notificationiconurl) {
+            notification.customdata.notificationiconurl = notification.senderImage;
+        }
+
+        const notificationMessage: AddonNotificationsNotificationMessage = {
+            id: notification.id ?? 0,
+            useridfrom: notification.userfromid ? Number(notification.userfromid) : USER_NOREPLY_USER,
+            userfromfullname: notification.userfromfullname ?? Translate.instant('core.noreplyname'),
+            useridto: notification.usertoid ? Number(notification.usertoid) : (siteInfo?.userid ?? 0),
+            usertofullname: siteInfo?.fullname ?? '',
+            subject: notification.title ?? '',
+            text: message,
+            fullmessage: message,
+            fullmessageformat: 1,
+            fullmessagehtml: message,
+            smallmessage: message,
+            notification: Number(notification.notif ?? 1),
+            contexturl: notification.contexturl || null,
+            contexturlname: null,
+            timecreated: Number(notification.date ?? 0),
+            timeread: 0,
+            component: notification.moodlecomponent,
+            customdata: notification.customdata ? JSON.stringify(notification.customdata) : undefined,
+        };
+
+        const formatted = await this.formatNotificationsData([notificationMessage]);
+
+        return formatted[0];
+    }
+
     /**
      * Function to format notification data.
      *
@@ -76,7 +119,7 @@ export class AddonNotificationsProvider {
             notification.read = notification.timeread > 0;
 
             if (typeof notification.customdata == 'string') {
-                notification.customdata = CoreTextUtils.parseJSON<Record<string, unknown>>(notification.customdata, {});
+                notification.customdata = CoreTextUtils.parseJSON<Record<string, string|number>>(notification.customdata, {});
             }
 
             // Try to set courseid the notification belongs to.
@@ -91,13 +134,16 @@ export class AddonNotificationsProvider {
 
             if (!notification.iconurl) {
                 // The iconurl is only returned in 4.0 or above. Calculate it if not present.
-                if (notification.component && notification.component.startsWith('mod_')) {
+                if (notification.moodlecomponent && notification.moodlecomponent.startsWith('mod_')) {
                     notification.iconurl = await CoreCourseModuleDelegate.getModuleIconSrc(
-                        notification.component.replace('mod_', ''),
+                        notification.moodlecomponent.replace('mod_', ''),
                     );
                 }
             }
 
+            const imgUrl = notification.customdata?.notificationpictureurl || notification.customdata?.notificationiconurl;
+            notification.imgUrl = imgUrl ? String(imgUrl) : undefined;
+
             if (notification.useridfrom > 0) {
                 // Try to get the profile picture of the user.
                 try {
@@ -502,8 +548,8 @@ export type AddonNotificationsNotificationMessage = {
     fullmessagehtml: string; // The message in html.
     smallmessage: string; // The shorten message.
     notification: number; // Is a notification?.
-    contexturl: string; // Context URL.
-    contexturlname: string; // Context URL link name.
+    contexturl: string | null; // Context URL.
+    contexturlname: string | null; // Context URL link name.
     timecreated: number; // Time created.
     timeread: number; // Time read.
     usertofullname: string; // User to full name.
@@ -532,15 +578,16 @@ export type AddonNotificationsGetUserNotificationPreferencesResult = {
  * Calculated data for messages returned by core_message_get_messages.
  */
 export type AddonNotificationsNotificationCalculatedData = {
-    mobiletext?: string; // Calculated in the app. Text to display for the notification.
+    mobiletext: string; // Calculated in the app. Text to display for the notification.
     moodlecomponent?: string; // Calculated in the app. Moodle's component.
-    notif?: number; // Calculated in the app. Whether it's a notification.
-    notification?: number; // Calculated in the app in some cases. Whether it's a notification.
-    read?: boolean; // Calculated in the app. Whether the notifications is read.
+    notif: number; // Calculated in the app. Whether it's a notification.
+    notification: number; // Calculated in the app in some cases. Whether it's a notification.
+    read: boolean; // Calculated in the app. Whether the notifications is read.
     courseid?: number; // Calculated in the app. Course the notification belongs to.
     profileimageurlfrom?: string; // Calculated in the app. Avatar of user that sent the notification.
     userfromfullname?: string; // Calculated in the app in some cases. User from full name.
-    customdata?: Record<string, unknown>; // Parsed custom data.
+    customdata?: Record<string, string|number>; // Parsed custom data.
+    imgUrl?: string; // Calculated in the app. URL of the image to use if the notification has no real user from.
 };
 
 /**
diff --git a/src/core/directives/format-text.ts b/src/core/directives/format-text.ts
index 05bf27fcf..77b09a000 100644
--- a/src/core/directives/format-text.ts
+++ b/src/core/directives/format-text.ts
@@ -280,7 +280,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
             button.setAttribute('aria-label', label);
             // Add an ion-icon item to apply the right styles, but the ion-icon component won't be executed.
             button.innerHTML = '<ion-icon name="fas-up-right-and-down-left-from-center" aria-hidden="true" \
-                src="assets/fonts/font-awesome/solid/expand-alt.svg">\
+                src="assets/fonts/font-awesome/solid/up-right-and-down-left-from-center.svg">\
             </ion-icon>';
 
             button.addEventListener('click', (e: Event) => {
diff --git a/src/core/features/comments/pages/viewer/viewer.ts b/src/core/features/comments/pages/viewer/viewer.ts
index f1e35112d..43a98924e 100644
--- a/src/core/features/comments/pages/viewer/viewer.ts
+++ b/src/core/features/comments/pages/viewer/viewer.ts
@@ -187,11 +187,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy {
 
             this.comments = comments.concat(this.comments);
 
-            this.comments.forEach((comment, index) => {
-                comment.showDate = this.showDate(comment, this.comments[index - 1]);
-                comment.showUserData = this.showUserData(comment, this.comments[index - 1]);
-                comment.showTail = this.showTail(comment, this.comments[index + 1]);
-            });
+            this.comments.forEach((comment, index) => this.calculateCommentData(comment, this.comments[index - 1]));
 
             this.canDeleteComments = this.addDeleteCommentsAvailable &&
                 (this.hasOffline || this.comments.some((comment) => !!comment.delete));
@@ -216,6 +212,18 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy {
 
     }
 
+    /**
+     * Calculate some comment data.
+     *
+     * @param comment Comment.
+     * @param prevComment Previous comment.
+     */
+    protected calculateCommentData(comment: CoreCommentsDataToDisplay, prevComment?: CoreCommentsDataToDisplay): void {
+        comment.showDate = this.showDate(comment, prevComment);
+        comment.showUserData = this.showUserData(comment, prevComment);
+        comment.showTail = this.showTail(comment, prevComment);
+    }
+
     /**
      * Function to load more commemts.
      *
@@ -245,17 +253,15 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy {
         this.refreshIcon = CoreConstants.ICON_LOADING;
         this.syncIcon = CoreConstants.ICON_LOADING;
 
-        try {
-            await this.invalidateComments();
-        } finally {
-            this.page = 0;
-            this.comments = [];
+        await CoreUtils.ignoreErrors(this.invalidateComments());
 
-            try {
-                await this.fetchComments(true, showErrors);
-            } finally {
-                refresher?.complete();
-            }
+        this.page = 0;
+        this.comments = [];
+
+        try {
+            await this.fetchComments(true, showErrors);
+        } finally {
+            refresher?.complete();
         }
     }
 
@@ -325,13 +331,11 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy {
             if (commentsResponse) {
                 this.invalidateComments();
 
-                const addedComments = await this.loadCommentProfile(commentsResponse);
-                addedComments.showDate = this.showDate(addedComments, this.comments[this.comments.length - 1]);
-                addedComments.showUserData = this.showUserData(addedComments, this.comments[this.comments.length - 1]);
-                addedComments.showTail = this.showTail(addedComments, this.comments[this.comments.length + 1]);
+                const addedComment = await this.loadCommentProfile(commentsResponse);
+                this.calculateCommentData(addedComment, this.comments[this.comments.length - 1]);
 
                 // Add the comment to the top.
-                this.comments = this.comments.concat([addedComments]);
+                this.comments = this.comments.concat([addedComment]);
                 this.canDeleteComments = this.addDeleteCommentsAvailable;
 
                 CoreEvents.trigger(CoreCommentsProvider.COMMENTS_COUNT_CHANGED_EVENT, {
@@ -343,6 +347,8 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy {
                     countChange: 1,
                 }, CoreSites.getCurrentSiteId());
 
+                this.refreshInBackground();
+
             } else if (commentsResponse === false) {
                 // Comments added in offline mode.
                 await this.loadOfflineData();
@@ -410,6 +416,8 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy {
                         area: this.area,
                         countChange: -1,
                     }, CoreSites.getCurrentSiteId());
+
+                    this.refreshInBackground();
                 }
             } else {
                 this.loadOfflineData();
@@ -602,6 +610,28 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy {
         this.showDelete = !this.showDelete;
     }
 
+    /**
+     * Refresh cached data in background.
+     */
+    protected async refreshInBackground(): Promise<void> {
+        await CoreUtils.ignoreErrors(this.invalidateComments());
+
+        const promises: Promise<unknown>[] = [];
+
+        for (let i = 0; i <= this.page; i++) {
+            promises.push(CoreComments.getComments(
+                this.contextLevel,
+                this.instanceId,
+                this.componentName,
+                this.itemId,
+                this.area,
+                i,
+            ));
+        }
+
+        await Promise.all(promises);
+    }
+
     /**
      * @inheritdoc
      */
diff --git a/src/core/features/pushnotifications/services/pushnotifications.ts b/src/core/features/pushnotifications/services/pushnotifications.ts
index 3f2ee4734..eb8fca182 100644
--- a/src/core/features/pushnotifications/services/pushnotifications.ts
+++ b/src/core/features/pushnotifications/services/pushnotifications.ts
@@ -460,7 +460,7 @@ export class CorePushNotificationsProvider {
             title: notification.title,
             message: notification.message,
             customdata: typeof rawData.customdata == 'string' ?
-                CoreTextUtils.parseJSON<Record<string, unknown>>(rawData.customdata, {}) : rawData.customdata,
+                CoreTextUtils.parseJSON<Record<string, string|number>>(rawData.customdata, {}) : rawData.customdata,
         });
 
         let site: CoreSite | undefined;
@@ -964,7 +964,7 @@ export type CorePushNotificationsNotificationBasicRawData = {
 export type CorePushNotificationsNotificationBasicData = Omit<CorePushNotificationsNotificationBasicRawData, 'customdata'> & {
     title?: string; // Notification title.
     message?: string; // Notification message.
-    customdata?: Record<string, unknown>; // Parsed custom data.
+    customdata?: Record<string, string|number>; // Parsed custom data.
 };
 
 /**
diff --git a/src/core/features/question/services/question-helper.ts b/src/core/features/question/services/question-helper.ts
index 846f4d640..5dc04b069 100644
--- a/src/core/features/question/services/question-helper.ts
+++ b/src/core/features/question/services/question-helper.ts
@@ -806,7 +806,7 @@ export class CoreQuestionHelperProvider {
                 newIcon.className = 'core-correct-icon ion-color ion-color-success questioncorrectnessicon';
             } else {
                 newIcon.setAttribute('name', 'fas-xmark');
-                newIcon.setAttribute('src', 'assets/fonts/font-awesome/solid/times.svg');
+                newIcon.setAttribute('src', 'assets/fonts/font-awesome/solid/xmark.svg');
                 newIcon.className = 'core-correct-icon ion-color ion-color-danger questioncorrectnessicon';
             }
 
diff --git a/src/core/features/tag/tag-lazy.module.ts b/src/core/features/tag/tag-lazy.module.ts
index 83079cbcb..a6f3b6c7a 100644
--- a/src/core/features/tag/tag-lazy.module.ts
+++ b/src/core/features/tag/tag-lazy.module.ts
@@ -45,7 +45,7 @@ function buildRoutes(injector: Injector): Routes {
         },
         {
             ...indexAreaRoute,
-            path: `${indexAreaRoute.path}/index`,
+            path: `index/${indexAreaRoute.path}`,
         },
     ];
 
diff --git a/src/core/features/user/pages/participants/participants.html b/src/core/features/user/pages/participants/participants.html
index 21ef547ed..fa9e23016 100644
--- a/src/core/features/user/pages/participants/participants.html
+++ b/src/core/features/user/pages/participants/participants.html
@@ -4,7 +4,7 @@
             <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
         </ion-refresher>
 
-        <core-search-box [disabled]="searchInProgress" [spellcheck]="false" [autoFocus]="true" [lengthCheck]="1" autocorrect="off"
+        <core-search-box [disabled]="searchInProgress" [spellcheck]="false" [lengthCheck]="1" autocorrect="off"
             searchArea="CoreUserParticipants" (onSubmit)="search($event)" (onClear)="clearSearch()">
         </core-search-box>
 
diff --git a/src/core/features/user/services/user.ts b/src/core/features/user/services/user.ts
index 3e9ca4b0f..7484e800b 100644
--- a/src/core/features/user/services/user.ts
+++ b/src/core/features/user/services/user.ts
@@ -59,6 +59,11 @@ export const USER_PROFILE_PICTURE_UPDATED = 'CoreUserProfilePictureUpdated';
  */
 export const USER_PROFILE_SERVER_TIMEZONE = '99';
 
+/**
+ * Fake ID for a "no reply" user.
+ */
+export const USER_NOREPLY_USER = -10;
+
 /**
  * Service to provide user functionalities.
  */
diff --git a/src/core/lang.json b/src/core/lang.json
index 913cf4c2f..6897372a9 100644
--- a/src/core/lang.json
+++ b/src/core/lang.json
@@ -216,6 +216,7 @@
     "nopermissionerror": "Sorry, but you do not currently have permissions to do that",
     "nopermissions": "Sorry, but you do not currently have permissions to do that ({{$a}}).",
     "nopermissiontoaccesspage": "You don't have permission to access this page.",
+    "noreplyname": "Do not reply to this email",
     "noresults": "No results",
     "noselection": "No selection",
     "notapplicable": "n/a",
diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss
index 8d599f0ad..1a8e8cd89 100644
--- a/src/theme/theme.base.scss
+++ b/src/theme/theme.base.scss
@@ -451,8 +451,8 @@ div.core-iframe-network-error {
         width: 50%;
         height: 50%;
         background-color: var(--danger);
-        -webkit-mask: url("/assets/fonts/font-awesome/solid/exclamation-triangle.svg") no-repeat 50% 50%;
-        mask: url("/assets/fonts/font-awesome/solid/exclamation-triangle.svg") no-repeat 50% 50%;
+        -webkit-mask: url("/assets/fonts/font-awesome/solid/triangle-exclamation.svg") no-repeat 50% 50%;
+        mask: url("/assets/fonts/font-awesome/solid/triangle-exclamation.svg") no-repeat 50% 50%;
     }
 }