diff --git a/src/addon/mod/feedback/pages/form/form.ts b/src/addon/mod/feedback/pages/form/form.ts
index 0d8bbf9fa..82e24a29b 100644
--- a/src/addon/mod/feedback/pages/form/form.ts
+++ b/src/addon/mod/feedback/pages/form/form.ts
@@ -24,6 +24,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
 import { CoreAppProvider } from '@providers/app';
 import { CoreEventsProvider } from '@providers/events';
 import { CoreCourseProvider } from '@core/course/providers/course';
+import { CoreCourseHelperProvider } from '@core/course/providers/helper';
 import { CoreLoginHelperProvider } from '@core/login/providers/helper';
 import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
 import { CoreSitesProvider } from '@providers/sites';
@@ -69,7 +70,7 @@ export class AddonModFeedbackFormPage implements OnDestroy {
             protected eventsProvider: CoreEventsProvider, protected feedbackSync: AddonModFeedbackSyncProvider, network: Network,
             protected translate: TranslateService, protected loginHelper: CoreLoginHelperProvider,
             protected linkHelper: CoreContentLinksHelperProvider, sitesProvider: CoreSitesProvider,
-            @Optional() private content: Content, zone: NgZone) {
+            @Optional() private content: Content, zone: NgZone, protected courseHelper: CoreCourseHelperProvider) {
 
         this.module = navParams.get('module');
         this.courseId = navParams.get('courseId');
@@ -325,10 +326,7 @@ export class AddonModFeedbackFormPage implements OnDestroy {
                 modal.dismiss();
             });
         } else {
-            // Use redirect to make the course the new history root (to avoid "loops" in history).
-            this.loginHelper.redirect('CoreCourseSectionPage', {
-                course: { id: this.courseId }
-            }, this.currentSite.getId());
+            this.courseHelper.getAndOpenCourse(undefined, this.courseId, {}, this.currentSite.getId());
         }
     }
 
diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json
index 8fd9e1499..8e7ff5128 100644
--- a/src/assets/lang/en.json
+++ b/src/assets/lang/en.json
@@ -1342,6 +1342,7 @@
     "core.favourites": "Starred",
     "core.filename": "Filename",
     "core.filenameexist": "File name already exists: {{$a}}",
+    "core.filenotfound": "File not found, sorry.",
     "core.fileuploader.addfiletext": "Add file",
     "core.fileuploader.audio": "Audio",
     "core.fileuploader.camera": "Camera",
diff --git a/src/core/course/providers/course.ts b/src/core/course/providers/course.ts
index e16318b09..c960b7c51 100644
--- a/src/core/course/providers/course.ts
+++ b/src/core/course/providers/course.ts
@@ -13,16 +13,20 @@
 // limitations under the License.
 
 import { Injectable } from '@angular/core';
+import { NavController } from 'ionic-angular';
 import { TranslateService } from '@ngx-translate/core';
 import { CoreAppProvider } from '@providers/app';
 import { CoreEventsProvider } from '@providers/events';
 import { CoreLoggerProvider } from '@providers/logger';
 import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites';
+import { CoreDomUtilsProvider } from '@providers/utils/dom';
 import { CoreTimeUtilsProvider } from '@providers/utils/time';
 import { CoreUtilsProvider } from '@providers/utils/utils';
 import { CoreSiteWSPreSets, CoreSite } from '@classes/site';
 import { CoreConstants } from '../../constants';
 import { CoreCourseOfflineProvider } from './course-offline';
+import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins';
+import { CoreCourseFormatDelegate } from './format-delegate';
 
 /**
  * Service that provides some features regarding a course.
@@ -96,7 +100,9 @@ export class CoreCourseProvider {
 
     constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private eventsProvider: CoreEventsProvider,
             private utils: CoreUtilsProvider, private timeUtils: CoreTimeUtilsProvider, private translate: TranslateService,
-            private courseOffline: CoreCourseOfflineProvider, private appProvider: CoreAppProvider) {
+            private courseOffline: CoreCourseOfflineProvider, private appProvider: CoreAppProvider,
+            private courseFormatDelegate: CoreCourseFormatDelegate, private sitePluginsProvider: CoreSitePluginsProvider,
+            private domUtils: CoreDomUtilsProvider) {
         this.logger = logger.getInstance('CoreCourseProvider');
 
         this.sitesProvider.registerSiteSchema(this.siteSchema);
@@ -899,6 +905,61 @@ export class CoreCourseProvider {
         return !!module.url;
     }
 
+    /**
+     * Wait for any course format plugin to load, and open the course page.
+     *
+     * If the plugin's promise is resolved, the course page will be opened.  If it is rejected, they will see an error.
+     * If the promise for the plugin is still in progress when the user tries to open the course, a loader
+     * will be displayed until it is complete, before the course page is opened.  If the promise is already complete,
+     * they will see the result immediately.
+     *
+     * This function must be in here instead of course helper to prevent circular dependencies.
+     *
+     * @param {NavController} navCtrl The nav controller to use. If not defined, the course will be opened in main menu.
+     * @param {any} course Course to open
+     * @param {any} [params] Other params to pass to the course page.
+     * @return {Promise<any>} Promise resolved when done.
+     */
+    openCourse(navCtrl: NavController, course: any, params?: any): Promise<any> {
+        const loading = this.domUtils.showModalLoading();
+
+        // Wait for site plugins to be fetched.
+        return this.sitePluginsProvider.waitFetchPlugins().then(() => {
+            if (this.sitePluginsProvider.sitePluginPromiseExists('format_' + course.format)) {
+                // This course uses a custom format plugin, wait for the format plugin to finish loading.
+
+                return this.sitePluginsProvider.sitePluginLoaded('format_' + course.format).then(() => {
+                    // The format loaded successfully, but the handlers wont be registered until all site plugins have loaded.
+                    if (this.sitePluginsProvider.sitePluginsFinishedLoading) {
+                        return this.courseFormatDelegate.openCourse(navCtrl, course, params);
+                    } else {
+                        // Wait for plugins to be loaded.
+                        const deferred = this.utils.promiseDefer(),
+                            observer = this.eventsProvider.on(CoreEventsProvider.SITE_PLUGINS_LOADED, () => {
+                                observer && observer.off();
+
+                                this.courseFormatDelegate.openCourse(navCtrl, course, params).then((response) => {
+                                    deferred.resolve(response);
+                                }).catch((error) => {
+                                    deferred.reject(error);
+                                });
+                            });
+
+                        return deferred.promise;
+                    }
+                }).catch(() => {
+                    // The site plugin failed to load. The user needs to restart the app to try loading it again.
+                    this.domUtils.showErrorModal('core.courses.errorloadplugins', true);
+                });
+            } else {
+                // No custom format plugin. We don't need to wait for anything.
+                return this.courseFormatDelegate.openCourse(navCtrl, course, params);
+            }
+        }).finally(() => {
+            loading.dismiss();
+        });
+    }
+
     /**
      * Change the course status, setting it to the previous status.
      *
diff --git a/src/core/course/providers/default-format.ts b/src/core/course/providers/default-format.ts
index 6cc8e1562..b445158a9 100644
--- a/src/core/course/providers/default-format.ts
+++ b/src/core/course/providers/default-format.ts
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import { Injectable } from '@angular/core';
+import { Injectable, Injector } from '@angular/core';
 import { NavController } from 'ionic-angular';
 import { CoreCoursesProvider } from '@core/courses/providers/courses';
+import { CoreLoginHelperProvider } from '@core/login/providers/helper';
 import { CoreCourseFormatHandler } from './format-delegate';
 
 /**
@@ -25,7 +26,9 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
     name = 'CoreCourseFormatDefault';
     format = 'default';
 
-    constructor(private coursesProvider: CoreCoursesProvider) { }
+    protected loginHelper: CoreLoginHelperProvider; // Inject it later to prevent circular dependencies.
+
+    constructor(protected coursesProvider: CoreCoursesProvider, protected injector: Injector) { }
 
     /**
      * Whether or not the handler is enabled on a site level.
@@ -154,7 +157,7 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
      * getCourseFormatComponent because it will display the course handlers at the top.
      * Your page should include the course handlers using CoreCoursesDelegate.
      *
-     * @param {NavController} navCtrl The NavController instance to use.
+     * @param {NavController} navCtrl The NavController instance to use. If not defined, please use loginHelper.redirect.
      * @param {any} course The course to open. It should contain a "format" attribute.
      * @param {any} [params] Params to pass to the course page.
      * @return {Promise<any>} Promise resolved when done.
@@ -163,7 +166,14 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
         params = params || {};
         Object.assign(params, { course: course });
 
-        return navCtrl.push('CoreCourseSectionPage', params);
+        if (navCtrl) {
+            return navCtrl.push('CoreCourseSectionPage', params);
+        } else {
+            // Open the course in the "phantom" tab.
+            this.loginHelper = this.loginHelper || this.injector.get(CoreLoginHelperProvider);
+
+            return this.loginHelper.redirect('CoreCourseSectionPage', params);
+        }
     }
 
     /**
diff --git a/src/core/course/providers/helper.ts b/src/core/course/providers/helper.ts
index be5872490..ba486f2a9 100644
--- a/src/core/course/providers/helper.ts
+++ b/src/core/course/providers/helper.ts
@@ -36,8 +36,6 @@ import { CoreLoginHelperProvider } from '@core/login/providers/helper';
 import { CoreConstants } from '@core/constants';
 import { CoreSite } from '@classes/site';
 import * as moment from 'moment';
-import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins';
-import { CoreCourseFormatDelegate } from '@core/course/providers/format-delegate';
 
 /**
  * Prefetch info of a module.
@@ -125,8 +123,7 @@ export class CoreCourseHelperProvider {
         private courseOptionsDelegate: CoreCourseOptionsDelegate, private siteHomeProvider: CoreSiteHomeProvider,
         private eventsProvider: CoreEventsProvider, private fileHelper: CoreFileHelperProvider,
         private appProvider: CoreAppProvider, private fileProvider: CoreFileProvider, private injector: Injector,
-        private coursesProvider: CoreCoursesProvider, private courseOffline: CoreCourseOfflineProvider,
-        private courseFormatDelegate: CoreCourseFormatDelegate, private sitePluginsProvider: CoreSitePluginsProvider) { }
+        private coursesProvider: CoreCoursesProvider, private courseOffline: CoreCourseOfflineProvider) { }
 
     /**
      * This function treats every module on the sections provided to load the handler data, treat completion
@@ -794,6 +791,30 @@ export class CoreCourseHelperProvider {
         });
     }
 
+    /**
+     * Get a course, wait for any course format plugin to load, and open the course page. It basically chains the functions
+     * getCourse and openCourse.
+     *
+     * @param {NavController} navCtrl The nav controller to use. If not defined, the course will be opened in main menu.
+     * @param {number} courseId Course ID.
+     * @param {any} [params] Other params to pass to the course page.
+     * @param {string} [siteId] Site ID. If not defined, current site.
+     */
+    getAndOpenCourse(navCtrl: NavController, courseId: number, params?: any, siteId?: string): Promise<any> {
+        const modal = this.domUtils.showModalLoading();
+
+        return this.getCourse(courseId, siteId).then((data) => {
+            return data.course;
+        }).catch(() => {
+            // Cannot get course, return a "fake".
+            return { id: courseId };
+        }).then((course) => {
+            modal.dismiss();
+
+            return this.openCourse(navCtrl, course, params, siteId);
+        });
+    }
+
     /**
      * Check if the course has a block with that name.
      *
@@ -1121,14 +1142,17 @@ export class CoreCourseHelperProvider {
                 // Check if site home is available.
                 return this.siteHomeProvider.isAvailable().then(() => {
                     this.loginHelper.redirect('CoreSiteHomeIndexPage', params, siteId);
+                }).finally(() => {
+                    modal.dismiss();
                 });
             } else {
-                this.loginHelper.redirect('CoreCourseSectionPage', params, siteId);
+                modal.dismiss();
+
+                return this.getAndOpenCourse(undefined, courseId, params, siteId);
             }
         }).catch((error) => {
-            this.domUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
-        }).finally(() => {
             modal.dismiss();
+            this.domUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
         });
     }
 
@@ -1399,46 +1423,22 @@ export class CoreCourseHelperProvider {
      * will be displayed until it is complete, before the course page is opened.  If the promise is already complete,
      * they will see the result immediately.
      *
-     * @param {NavController} navCtrl The nav controller to use.
+     * @param {NavController} navCtrl The nav controller to use. If not defined, the course will be opened in main menu.
      * @param {any} course Course to open
      * @param {any} [params] Params to pass to the course page.
+     * @param {string} [siteId] Site ID. If not defined, current site.
      * @return {Promise<any>} Promise resolved when done.
      */
-    openCourse(navCtrl: NavController, course: any, params?: any): Promise<any> {
-        if (this.sitePluginsProvider.sitePluginPromiseExists('format_' + course.format)) {
-            // This course uses a custom format plugin, wait for the format plugin to finish loading.
-            const loading = this.domUtils.showModalLoading();
-
-            return this.sitePluginsProvider.sitePluginLoaded('format_' + course.format).then(() => {
-                // The format loaded successfully, but the handlers wont be registered until all site plugins have loaded.
-                if (this.sitePluginsProvider.sitePluginsFinishedLoading) {
-                    loading.dismiss();
-
-                    return this.courseFormatDelegate.openCourse(navCtrl, course, params);
-                } else {
-                    // Wait for plugins to be loaded.
-                    const deferred = this.utils.promiseDefer(),
-                        observer = this.eventsProvider.on(CoreEventsProvider.SITE_PLUGINS_LOADED, () => {
-                            loading.dismiss();
-                            observer && observer.off();
-
-                            this.courseFormatDelegate.openCourse(navCtrl, course, params).then((response) => {
-                                deferred.resolve(response);
-                            }).catch((error) => {
-                                deferred.reject(error);
-                            });
-                        });
-
-                    return deferred.promise;
-                }
-            }).catch(() => {
-                // The site plugin failed to load. The user needs to restart the app to try loading it again.
-                loading.dismiss();
-                this.domUtils.showErrorModal('core.courses.errorloadplugins', true);
-            });
+    openCourse(navCtrl: NavController, course: any, params?: any, siteId?: string): Promise<any> {
+        if (!siteId || siteId == this.sitesProvider.getCurrentSiteId()) {
+            // Current site, we can open the course.
+            return this.courseProvider.openCourse(navCtrl, course, params);
         } else {
-            // No custom format plugin. We don't need to wait for anything.
-            return this.courseFormatDelegate.openCourse(navCtrl, course, params);
+            // We need to load the site first.
+            params = params || {};
+            Object.assign(params, { course: course });
+
+            return this.loginHelper.redirect(CoreLoginHelperProvider.OPEN_COURSE, params, siteId);
         }
     }
 }
diff --git a/src/core/courses/providers/course-link-handler.ts b/src/core/courses/providers/course-link-handler.ts
index 092ac130d..010dcdbb2 100644
--- a/src/core/courses/providers/course-link-handler.ts
+++ b/src/core/courses/providers/course-link-handler.ts
@@ -19,8 +19,8 @@ import { CoreDomUtilsProvider } from '@providers/utils/dom';
 import { CoreTextUtilsProvider } from '@providers/utils/text';
 import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
 import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
-import { CoreLoginHelperProvider } from '@core/login/providers/helper';
 import { CoreCourseProvider } from '@core/course/providers/course';
+import { CoreCourseHelperProvider } from '@core/course/providers/helper';
 import { CoreCoursesProvider } from './courses';
 
 /**
@@ -34,9 +34,9 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
     protected waitStart = 0;
 
     constructor(private sitesProvider: CoreSitesProvider, private coursesProvider: CoreCoursesProvider,
-            private loginHelper: CoreLoginHelperProvider, private domUtils: CoreDomUtilsProvider,
+            private domUtils: CoreDomUtilsProvider,
             private translate: TranslateService, private courseProvider: CoreCourseProvider,
-            private textUtils: CoreTextUtilsProvider) {
+            private textUtils: CoreTextUtilsProvider, private courseHelper: CoreCourseHelperProvider) {
         super();
     }
 
@@ -55,7 +55,6 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
 
         const sectionId = params.sectionid ? parseInt(params.sectionid, 10) : null,
             pageParams: any = {
-                course: { id: courseId },
                 sectionId: sectionId || null
             };
         let sectionNumber = typeof params.section != 'undefined' ? parseInt(params.section, 10) : NaN;
@@ -80,8 +79,8 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
                         // Ignore errors.
                     });
                 } else {
-                    // Use redirect to make the course the new history root (to avoid "loops" in history).
-                    this.loginHelper.redirect('CoreCourseSectionPage', pageParams, siteId);
+                    // Don't pass the navCtrl to make the course the new history root (to avoid "loops" in history).
+                    this.courseHelper.getAndOpenCourse(undefined, courseId, pageParams, siteId);
                 }
             }
         }];
@@ -121,9 +120,12 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
     protected actionEnrol(courseId: number, url: string, pageParams: any): Promise<any> {
         const modal = this.domUtils.showModalLoading(),
             isEnrolUrl = !!url.match(/(\/enrol\/index\.php)|(\/course\/enrol\.php)/);
+        let course;
 
         // Check if user is enrolled in the course.
-        return this.coursesProvider.getUserCourse(courseId).catch(() => {
+        return this.coursesProvider.getUserCourse(courseId).then((courseObj) => {
+            course = courseObj;
+        }).catch(() => {
             // User is not enrolled in the course. Check if can self enrol.
             return this.canSelfEnrol(courseId).then(() => {
                 modal.dismiss();
@@ -134,7 +136,9 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
 
                 return promise.then(() => {
                     // Enrol URL or user confirmed.
-                    return this.selfEnrol(courseId).catch((error) => {
+                    return this.selfEnrol(courseId).then((courseObj) => {
+                        course = courseObj;
+                    }).catch((error) => {
                         if (error) {
                             this.domUtils.showErrorModal(error);
                         }
@@ -170,10 +174,22 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
                 });
             });
         }).then(() => {
+            // Check if we need to retrieve the course.
+            if (!course) {
+                return this.courseHelper.getCourse(courseId).then((data) => {
+                    return data.course;
+                }).catch(() => {
+                    // Cannot get course, return a "fake".
+                    return { id: courseId };
+                });
+            }
+
+            return course;
+        }).then((course) => {
             modal.dismiss();
 
-            // Use redirect to make the course the new history root (to avoid "loops" in history).
-            this.loginHelper.redirect('CoreCourseSectionPage', pageParams, this.sitesProvider.getCurrentSiteId());
+            // Now open the course.
+            this.courseHelper.openCourse(undefined, course, pageParams);
         });
     }
 
diff --git a/src/core/login/providers/helper.ts b/src/core/login/providers/helper.ts
index 21154313c..25e312ab8 100644
--- a/src/core/login/providers/helper.ts
+++ b/src/core/login/providers/helper.ts
@@ -28,6 +28,7 @@ import { CoreTextUtilsProvider } from '@providers/utils/text';
 import { CoreUrlUtilsProvider } from '@providers/utils/url';
 import { CoreUtilsProvider } from '@providers/utils/utils';
 import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins';
+import { CoreCourseProvider } from '@core/course/providers/course';
 import { CoreConfigConstants } from '../../../configconstants';
 import { CoreConstants } from '@core/constants';
 import { Md5 } from 'ts-md5/dist/md5';
@@ -72,6 +73,8 @@ export interface CoreLoginSSOData {
  */
 @Injectable()
 export class CoreLoginHelperProvider {
+    static OPEN_COURSE = 'open_course';
+
     protected logger;
     protected isSSOConfirmShown = false;
     protected isOpenEditAlertShown = false;
@@ -83,7 +86,7 @@ export class CoreLoginHelperProvider {
             private eventsProvider: CoreEventsProvider, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider,
             private urlUtils: CoreUrlUtilsProvider, private configProvider: CoreConfigProvider, private platform: Platform,
             private initDelegate: CoreInitDelegate, private sitePluginsProvider: CoreSitePluginsProvider,
-            private location: Location, private alertCtrl: AlertController) {
+            private location: Location, private alertCtrl: AlertController, private courseProvider: CoreCourseProvider) {
         this.logger = logger.getInstance('CoreLoginHelper');
     }
 
@@ -423,13 +426,7 @@ export class CoreLoginHelperProvider {
      * @return {Promise<any>} Promise resolved when done.
      */
     goToSiteInitialPage(navCtrl?: NavController, page?: string, params?: any, options?: NavOptions): Promise<any> {
-        navCtrl = navCtrl || this.appProvider.getRootNavController();
-
-        // Due to DeepLinker, we need to remove the path from the URL before going to main menu.
-        // IonTabs checks the URL to determine which path to load for deep linking, so we clear the URL.
-        this.location.replaceState('');
-
-        return navCtrl.setRoot('CoreMainMenuPage', { redirectPage: page, redirectParams: params }, options);
+        return this.openMainMenu(navCtrl, page, params, options);
     }
 
     /**
@@ -604,11 +601,7 @@ export class CoreLoginHelperProvider {
 
             return this.sitesProvider.loadSite(siteId, page, params).then((loggedIn) => {
                 if (loggedIn) {
-                    // Due to DeepLinker, we need to remove the path from the URL before going to main menu.
-                    // IonTabs checks the URL to determine which path to load for deep linking, so we clear the URL.
-                    this.location.replaceState('');
-
-                    return navCtrl.setRoot('CoreMainMenuPage', { redirectPage: page, redirectParams: params });
+                    return this.openMainMenu(navCtrl, page, params);
                 }
             }).catch((error) => {
                 // Site doesn't exist.
@@ -626,7 +619,39 @@ export class CoreLoginHelperProvider {
      * @param {any} params Params to pass to the page.
      */
     protected loadPageInMainMenu(page: string, params: any): void {
-        this.eventsProvider.trigger(CoreEventsProvider.LOAD_PAGE_MAIN_MENU, { redirectPage: page, redirectParams: params });
+        if (page == CoreLoginHelperProvider.OPEN_COURSE) {
+            // Use the openCourse function.
+            this.courseProvider.openCourse(undefined, params.course, params);
+        } else {
+            this.eventsProvider.trigger(CoreEventsProvider.LOAD_PAGE_MAIN_MENU, { redirectPage: page, redirectParams: params });
+        }
+    }
+
+    /**
+     * Open the main menu, loading a certain page.
+     *
+     * @param {NavController} navCtrl NavController.
+     * @param {string} page Name of the page to load.
+     * @param {any} params Params to pass to the page.
+     * @param {NavOptions} [options] Navigation options.
+     * @return {Promise<any>} Promise resolved when done.
+     */
+    protected openMainMenu(navCtrl: NavController, page: string, params: any, options?: NavOptions): Promise<any> {
+        navCtrl = navCtrl || this.appProvider.getRootNavController();
+
+        // Due to DeepLinker, we need to remove the path from the URL before going to main menu.
+        // IonTabs checks the URL to determine which path to load for deep linking, so we clear the URL.
+        this.location.replaceState('');
+
+        if (page == CoreLoginHelperProvider.OPEN_COURSE) {
+            // Load the main menu first, and then open the course.
+            return navCtrl.setRoot('CoreMainMenuPage').finally(() => {
+                return this.courseProvider.openCourse(undefined, params.course, params);
+            });
+        } else {
+            // Open the main menu.
+            return navCtrl.setRoot('CoreMainMenuPage', { redirectPage: page, redirectParams: params }, options);
+        }
     }
 
     /**
@@ -793,7 +818,7 @@ export class CoreLoginHelperProvider {
     /**
      * Redirect to a new page, setting it as the root page and loading the right site if needed.
      *
-     * @param {string} page Name of the page to load.
+     * @param {string} page Name of the page to load. Special cases: OPEN_COURSE (to open course page).
      * @param {any} params Params to pass to the page.
      * @param {string} [siteId] Site to load. If not defined, current site.
      * @return {Promise<any>} Promise resolved when done.
diff --git a/src/core/siteplugins/providers/helper.ts b/src/core/siteplugins/providers/helper.ts
index cc78d9509..395b777d5 100644
--- a/src/core/siteplugins/providers/helper.ts
+++ b/src/core/siteplugins/providers/helper.ts
@@ -107,8 +107,9 @@ export class CoreSitePluginsHelperProvider {
                     }).finally(() => {
                         eventsProvider.trigger(CoreEventsProvider.SITE_PLUGINS_LOADED, {}, data.siteId);
                     });
-
                 }
+            }).finally(() => {
+                this.sitePluginsProvider.setPluginsFetched();
             });
         });
 
diff --git a/src/core/siteplugins/providers/siteplugins.ts b/src/core/siteplugins/providers/siteplugins.ts
index 43fd975c7..20036c9be 100644
--- a/src/core/siteplugins/providers/siteplugins.ts
+++ b/src/core/siteplugins/providers/siteplugins.ts
@@ -21,7 +21,7 @@ import { CoreLoggerProvider } from '@providers/logger';
 import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
 import { CoreSitesProvider } from '@providers/sites';
 import { CoreTextUtilsProvider } from '@providers/utils/text';
-import { CoreUtilsProvider } from '@providers/utils/utils';
+import { CoreUtilsProvider, PromiseDefer } from '@providers/utils/utils';
 import { CoreConfigConstants } from '../../../configconstants';
 import { CoreCoursesProvider } from '@core/courses/providers/courses';
 import { CoreEventsProvider } from '@providers/events';
@@ -67,6 +67,7 @@ export class CoreSitePluginsProvider {
     protected logger;
     protected sitePlugins: {[name: string]: CoreSitePluginsHandler} = {}; // Site plugins registered.
     protected sitePluginPromises: {[name: string]: Promise<any>} = {}; // Promises of loading plugins.
+    protected fetchPluginsDeferred: PromiseDefer;
     hasSitePluginsLoaded = false;
     sitePluginsFinishedLoading = false;
 
@@ -75,10 +76,17 @@ export class CoreSitePluginsProvider {
             private filepoolProvider: CoreFilepoolProvider, private coursesProvider: CoreCoursesProvider,
             private textUtils: CoreTextUtilsProvider, private eventsProvider: CoreEventsProvider) {
         this.logger = logger.getInstance('CoreUserProvider');
+
         const observer = this.eventsProvider.on(CoreEventsProvider.SITE_PLUGINS_LOADED, () => {
             this.sitePluginsFinishedLoading = true;
             observer && observer.off();
         });
+
+        // Initialize deferred at start and on logout.
+        this.fetchPluginsDeferred = this.utils.promiseDefer();
+        eventsProvider.on(CoreEventsProvider.LOGOUT, () => {
+            this.fetchPluginsDeferred = this.utils.promiseDefer();
+        });
     }
 
     /**
@@ -531,6 +539,13 @@ export class CoreSitePluginsProvider {
         this.sitePluginPromises[component] = promise;
     }
 
+    /**
+     * Set plugins fetched.
+     */
+    setPluginsFetched(): void {
+        this.fetchPluginsDeferred.resolve();
+    }
+
     /**
      * Is a plugin being initialised for the specified component?
      *
@@ -550,4 +565,13 @@ export class CoreSitePluginsProvider {
     sitePluginLoaded(component: string): Promise<any> {
         return this.sitePluginPromises[component];
     }
+
+    /**
+     * Wait for fetch plugins to be done.
+     *
+     * @return {Promise<any>} Promise resolved when site plugins have been fetched.
+     */
+    waitFetchPlugins(): Promise<any> {
+        return this.fetchPluginsDeferred.promise;
+    }
 }