diff --git a/scripts/langindex.json b/scripts/langindex.json
index e1beb3757..092fbfb2c 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -1517,10 +1517,8 @@
   "core.course": "moodle",
   "core.course.activitydisabled": "local_moodlemobileapp",
   "core.course.activitynotyetviewableremoteaddon": "local_moodlemobileapp",
-  "core.course.activitynotyetviewablesiteupgradeneeded": "local_moodlemobileapp",
   "core.course.allsections": "local_moodlemobileapp",
   "core.course.aria:sectionprogress": "local_moodlemobileapp",
-  "core.course.askadmintosupport": "local_moodlemobileapp",
   "core.course.availablespace": "local_moodlemobileapp",
   "core.course.cannotdeletewhiledownloading": "local_moodlemobileapp",
   "core.course.completion_automatic:done": "course",
@@ -2357,7 +2355,6 @@
   "core.whatisyourage": "moodle",
   "core.wheredoyoulive": "moodle",
   "core.whoissiteadmin": "local_moodlemobileapp",
-  "core.whoops": "local_moodlemobileapp",
   "core.whyisthishappening": "local_moodlemobileapp",
   "core.whyisthisrequired": "moodle",
   "core.wsfunctionnotavailable": "local_moodlemobileapp",
diff --git a/src/addons/calendar/components/calendar/calendar.scss b/src/addons/calendar/components/calendar/calendar.scss
index 35cd3ccbd..057745e3b 100644
--- a/src/addons/calendar/components/calendar/calendar.scss
+++ b/src/addons/calendar/components/calendar/calendar.scss
@@ -133,20 +133,10 @@
         margin-right: 1px;
         margin-left: 1px;
 
-        &.calendar_event_category {
-            background-color: var(--addon-calendar-event-category-color);
-        }
-        &.calendar_event_course {
-            background-color: var(--addon-calendar-event-course-color);
-        }
-        &.calendar_event_group {
-            background-color: var(--addon-calendar-event-group-color);
-        }
-        &.calendar_event_user {
-            background-color: var(--addon-calendar-event-user-color);
-        }
-        &.calendar_event_site {
-            background-color: var(--addon-calendar-event-site-color);
+        @each $category, $value in $calendar-event-category-colors {
+            &.calendar_event_#{$category} {
+                background-color: $value;
+            }
         }
     }
 
diff --git a/src/addons/storagemanager/services/handlers/course-menu.ts b/src/addons/storagemanager/services/handlers/course-menu.ts
index 7aa36c6de..74dc8f409 100644
--- a/src/addons/storagemanager/services/handlers/course-menu.ts
+++ b/src/addons/storagemanager/services/handlers/course-menu.ts
@@ -48,7 +48,7 @@ export class AddonStorageManagerCourseMenuHandlerService implements CoreCourseOp
         course: CoreCourseAnyCourseDataWithOptions,
     ): CoreCourseOptionsMenuHandlerData {
         return {
-            icon: 'cloud-download',
+            icon: 'fas-cloud-download-alt',
             title: 'addon.storagemanager.coursedownloads',
             page: 'storage/' + course.id,
             pageParams: {
diff --git a/src/assets/fonts/moodle/font-awesome/cloud-done.svg b/src/assets/fonts/moodle/font-awesome/cloud-done.svg
new file mode 100644
index 000000000..9f80a5392
--- /dev/null
+++ b/src/assets/fonts/moodle/font-awesome/cloud-done.svg
@@ -0,0 +1 @@
+
-         
 
     
     
-         
 
     
      {
-        let library = 'ionic';
+        let library = '';
         let iconName = this.name;
+        let font = 'ionicons';
         const parts = iconName.split('-', 2);
         if (parts.length == 2) {
             switch (parts[0]) {
                 case 'far':
                     library = 'regular';
-                    iconName = iconName.substring(4);
+                    font = 'font-awesome';
                     break;
                 case 'fa':
                 case 'fas':
                     library = 'solid';
-                    iconName = iconName.substring(parts[0].length + 1);
+                    font = 'font-awesome';
                     break;
                 case 'fab':
                     library = 'brands';
-                    iconName = iconName.substring(4);
+                    font = 'font-awesome';
+                    break;
+                case 'moodle':
+                    library = 'moodle';
+                    font = 'moodle';
+                    break;
+                case 'fam':
+                    library = 'font-awesome';
+                    font = 'moodle';
                     break;
                 default:
                     break;
             }
         }
 
-        if (library != 'ionic') {
-            const src = `assets/fonts/font-awesome/${library}/${iconName}.svg`;
-            this.element.setAttribute('src', src);
-            this.element.classList.add('faicon');
-
-            if (CoreConstants.BUILD.isDevelopment || CoreConstants.BUILD.isTesting) {
-                try {
-                    await Http.get(src, { responseType: 'text' }).toPromise();
-                } catch (error) {
-                    this.logger.error(`Icon ${this.name} not found`);
-                }
-            }
-        } else {
+        if (font == 'ionicons') {
             this.element.removeAttribute('src');
             this.logger.warn(`Ionic icon ${this.name} detected`);
+
+            return;
         }
 
-        return;
+        iconName = iconName.substring(parts[0].length + 1);
+
+        const src = `assets/fonts/${font}/${library}/${iconName}.svg`;
+        this.element.setAttribute('src', src);
+        this.element.classList.add('faicon');
+
+        if (CoreConstants.BUILD.isDevelopment || CoreConstants.BUILD.isTesting) {
+            try {
+                await Http.get(src, { responseType: 'text' }).toPromise();
+            } catch (error) {
+                this.logger.error(`Icon ${this.name} not found`);
+            }
+        }
     }
 
     /**
diff --git a/src/core/features/course/components/module-info/core-course-module-info.html b/src/core/features/course/components/module-info/core-course-module-info.html
index 256f2c789..f6f50dd35 100644
--- a/src/core/features/course/components/module-info/core-course-module-info.html
+++ b/src/core/features/course/components/module-info/core-course-module-info.html
@@ -7,6 +7,8 @@
             
              
+            
+
+
     
-        
-        
-            
-                {{ date.label }}  {{ date.timestamp * 1000 |
-                coreFormatDate:'strftimedatetime' }}
-            
-        
          
      
  
+
+
+    
+    
+        
+            {{ date.label }}  {{ date.timestamp
+            *
+            1000 | coreFormatDate:'strftimedatetime' }}
+        
+    
+    
+    
+        
+         
+    
+
                 
                     
-                         
                  
@@ -81,7 +81,7 @@
              
             
-                
                     {{ 'core.download' | translate }}
diff --git a/src/core/features/course/components/module/core-course-module.html b/src/core/features/course/components/module/core-course-module.html
index e1048efe5..579f101e5 100644
--- a/src/core/features/course/components/module/core-course-module.html
+++ b/src/core/features/course/components/module/core-course-module.html
@@ -17,6 +17,8 @@
                     
                         
 
                 
diff --git a/src/core/features/course/components/module/module.scss b/src/core/features/course/components/module/module.scss
index da557da41..c4fd942bd 100644
--- a/src/core/features/course/components/module/module.scss
+++ b/src/core/features/course/components/module/module.scss
@@ -13,6 +13,7 @@
 
         .core-module-title .item-heading ion-icon {
             @include margin-horizontal(8px, null);
+            vertical-align: middle;
         }
 
         .core-module-buttons,
diff --git a/src/core/features/course/components/module/module.ts b/src/core/features/course/components/module/module.ts
index 0761f2ad6..478dc0b62 100644
--- a/src/core/features/course/components/module/module.ts
+++ b/src/core/features/course/components/module/module.ts
@@ -22,6 +22,12 @@ import {
 } from '@features/course/services/course-helper';
 import { CoreCourse } from '@features/course/services/course';
 import { CoreCourseModuleDelegate, CoreCourseModuleHandlerButton } from '@features/course/services/module-delegate';
+import {
+    CoreCourseModulePrefetchDelegate,
+    CoreCourseModulePrefetchHandler,
+} from '@features/course/services/module-prefetch-delegate';
+import { CoreConstants } from '@/core/constants';
+import { CoreEventObserver, CoreEvents } from '@singletons/events';
 
 /**
  * Component to display a module entry in a list of modules.
@@ -47,11 +53,16 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
     hasInfo = false;
     showLegacyCompletion = false; // Whether to show module completion in the old format.
     showManualCompletion = false; // Whether to show manual completion when completion conditions are disabled.
+    prefetchStatusIcon = ''; // Module prefetch status icon.
+    prefetchStatusText = ''; // Module prefetch status text.
+    protected prefetchHandler?: CoreCourseModulePrefetchHandler;
+
+    protected moduleStatusObserver?: CoreEventObserver;
 
     /**
-     * Component being initialized.
+     * @inheritdoc
      */
-    ngOnInit(): void {
+    async ngOnInit(): Promise
 {
         this.modNameTranslated = CoreCourse.translateModuleName(this.module.modname) || '';
         this.showLegacyCompletion = !CoreSites.getCurrentSite()?.isVersionGreaterEqualThan('3.11');
         this.checkShowManualCompletion();
@@ -70,6 +81,60 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
             (this.module.visible !== 0 && this.module.isStealth) ||
             (this.module.availabilityinfo)
         );
+
+        if (this.module.handlerData?.showDownloadButton) {
+            const status = await CoreCourseModulePrefetchDelegate.getModuleStatus(this.module, this.module.course);
+            this.updateModuleStatus(status);
+
+            // Listen for changes on this module status, even if download isn't enabled.
+            this.prefetchHandler = CoreCourseModulePrefetchDelegate.getPrefetchHandlerFor(this.module.modname);
+            if (!this.prefetchHandler) {
+                return;
+            }
+
+            this.moduleStatusObserver = CoreEvents.on(CoreEvents.PACKAGE_STATUS_CHANGED, (data) => {
+                if (this.module.id != data.componentId || data.component != this.prefetchHandler?.component) {
+                    return;
+                }
+
+                let status = data.status;
+                if (this.prefetchHandler.determineStatus) {
+                    // Call determineStatus to get the right status to display.
+                    status = this.prefetchHandler.determineStatus(this.module, status, true);
+                }
+
+                // Update the status.
+                this.updateModuleStatus(status);
+            }, CoreSites.getCurrentSiteId());
+        }
+    }
+
+    /**
+     * Show module status.
+     *
+     * @param prefetchstatus Module status.
+     */
+    protected updateModuleStatus(prefetchstatus: string): void {
+        if (!prefetchstatus) {
+            return;
+        }
+
+        switch (prefetchstatus) {
+            case CoreConstants.OUTDATED:
+                this.prefetchStatusIcon = CoreConstants.ICON_OUTDATED;
+                this.prefetchStatusText = 'core.outdated';
+                break;
+            case CoreConstants.DOWNLOADED:
+                this.prefetchStatusIcon = CoreConstants.ICON_DOWNLOADED;
+                this.prefetchStatusText = 'core.downloaded';
+                break;
+            default:
+                this.prefetchStatusIcon = '';
+                this.prefetchStatusText = '';
+                break;
+        }
+
+        this.module.handlerData?.updateStatus?.(prefetchstatus);
     }
 
     /**
@@ -109,10 +174,11 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
     }
 
     /**
-     * Component destroyed.
+     * @inheritdoc
      */
     ngOnDestroy(): void {
         this.module.handlerData?.onDestroy?.();
+        this.moduleStatusObserver?.off();
     }
 
 }
diff --git a/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html b/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html
index 4fd22a35d..f8b7bd52f 100644
--- a/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html
+++ b/src/core/features/course/components/unsupported-module/core-course-unsupported-module.html
@@ -1,21 +1,22 @@
-
-    
{{ 'core.whoops' | translate }} 
-    
{{ 'core.uhoh' | translate }} 
-
-    
{{ 'core.course.activitydisabled' | translate }}
-    
-        {{ 'core.course.activitynotyetviewablesiteupgradeneeded' | translate }}
-    
-    
-        {{ 'core.course.activitynotyetviewableremoteaddon' | translate }}
-    
-    
{{ 'core.course.askadmintosupport' | translate }} 
-
-    
-        
{{ 'core.course.useactivityonbrowser' | translate }} 
-        
+
+    
+        
+            {{ 'core.uhoh' | translate }}  
+                {{ 'core.course.activitydisabled' | translate }} 
+                
+                    {{ 'core.course.activitynotyetviewableremoteaddon' | translate }}
+                 
+            
+         
+     
+ 
+
+    
+        {{ 'core.course.useactivityonbrowser' | translate }}
+        
             {{ 'core.openinbrowser' | translate }}
              
-       
-
-            
+            
                  
           
@@ -24,44 +24,24 @@
     
     
         
+            [componentId]="module.id" [expandDescription]="true" [showAvailabilityInfo]="true">
+            
+                
+                     
 
-            
-                
-                    
-                        
-                             
-                    
-
-                    
-                    
-                        
-                            {{ 'core.course.hiddenfromstudents' | translate }}
-                         
-                    
-                    
-                        
-                            {{ 'core.course.hiddenoncoursepage' | translate }}
-                         
-                    
-
-                    
-                    
-                        
-                         
-                    
-                 
-             
-
-            
+                
+                
+                    {{ 'core.course.hiddenfromstudents' | translate }}
+                 
+                
+                    {{ 'core.course.hiddenoncoursepage' | translate }}
+                 
+            
 
+
+         
 
      void; // Update the module after a certain time.
@@ -80,6 +81,8 @@ export class CoreCourseModulePreviewPage implements OnInit {
         if (!this.unsupported) {
             this.module.handlerData =
                 await CoreCourseModuleDelegate.getModuleDataFor(this.module.modname, this.module, this.courseId);
+        } else {
+            this.isDisabledInSite = CoreCourseModuleDelegate.isModuleDisabledInSite(this.module.modname);
         }
 
         this.title = this.module.name;
diff --git a/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html b/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html
index c63576239..39bf2b5e2 100644
--- a/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html
+++ b/src/core/features/courses/components/course-list-item/core-courses-course-list-item.html
@@ -62,8 +62,8 @@
                         
                     
 
-                    
              -1) {
             row.itemtype = 'agg_mean';
-            row.image = 'assets/img/grades/agg_mean.svg';
+            row.icon = 'moodle-agg_mean';
             row.iconAlt = Translate.instant('core.grades.aggregatemean');
         } else if (text.indexOf('/agg_sum') > -1) {
             row.itemtype = 'agg_sum';
-            row.image = 'assets/img/grades/agg_sum.svg';
+            row.icon = 'moodle-agg_sum';
             row.iconAlt = Translate.instant('core.grades.aggregatesum');
         } else if (text.indexOf('/outcomes') > -1 || text.indexOf('fa-tasks') > -1) {
             row.itemtype = 'outcome';
diff --git a/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html b/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html
index d4f915a10..94574fcbe 100644
--- a/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html
+++ b/src/core/features/siteplugins/components/module-index/core-siteplugins-module-index.html
@@ -5,6 +5,10 @@
      
 
 
+
+
+ 
+
 
diff --git a/src/core/features/siteplugins/pages/module-index/module-index.html b/src/core/features/siteplugins/pages/module-index/module-index.html
index 7bbd67a91..58f3efc7c 100644
--- a/src/core/features/siteplugins/pages/module-index/module-index.html
+++ b/src/core/features/siteplugins/pages/module-index/module-index.html
@@ -1,4 +1,4 @@
-
+