diff --git a/src/core/features/courses/courses.module.ts b/src/core/features/courses/courses.module.ts
index 3f5014a11..e47bfb9f7 100644
--- a/src/core/features/courses/courses.module.ts
+++ b/src/core/features/courses/courses.module.ts
@@ -19,7 +19,7 @@ import { CoreMainMenuHomeRoutingModule } from '@features/mainmenu/pages/home/hom
import { CoreMainMenuHomeDelegate } from '@features/mainmenu/services/home-delegate';
import { CoreDashboardHomeHandler, CoreDashboardHomeHandlerService } from './services/handlers/dashboard-home';
-import { CoreCoursesMyCoursesHomeHandler, CoreCoursesMyCoursesHomeHandlerService } from './services/handlers/my-courses.home';
+import { CoreCoursesMyCoursesHomeHandler, CoreCoursesMyCoursesHomeHandlerService } from './services/handlers/my-courses-home';
const mainMenuHomeChildrenRoutes: Routes = [
{
diff --git a/src/core/features/courses/pages/dashboard/dashboard.html b/src/core/features/courses/pages/dashboard/dashboard.html
index 53ab4c47d..5e0488613 100644
--- a/src/core/features/courses/pages/dashboard/dashboard.html
+++ b/src/core/features/courses/pages/dashboard/dashboard.html
@@ -12,8 +12,19 @@
-
-
- Dashboard
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/core/features/courses/pages/dashboard/dashboard.module.ts b/src/core/features/courses/pages/dashboard/dashboard.module.ts
index 5ba48f8f2..40924f020 100644
--- a/src/core/features/courses/pages/dashboard/dashboard.module.ts
+++ b/src/core/features/courses/pages/dashboard/dashboard.module.ts
@@ -20,6 +20,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { CoreComponentsModule } from '@components/components.module';
import { CoreDirectivesModule } from '@directives/directives.module';
+import { CoreBlockComponentsModule } from '@features/block/components/components.module';
import { CoreCoursesDashboardPage } from './dashboard';
@@ -38,6 +39,7 @@ const routes: Routes = [
TranslateModule.forChild(),
CoreComponentsModule,
CoreDirectivesModule,
+ CoreBlockComponentsModule,
],
declarations: [
CoreCoursesDashboardPage,
diff --git a/src/core/features/courses/pages/dashboard/dashboard.ts b/src/core/features/courses/pages/dashboard/dashboard.ts
index 997f5e5ac..cc93164a6 100644
--- a/src/core/features/courses/pages/dashboard/dashboard.ts
+++ b/src/core/features/courses/pages/dashboard/dashboard.ts
@@ -12,12 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { Component, OnDestroy, OnInit } from '@angular/core';
-import { NavController } from '@ionic/angular';
+import { Component, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core';
+import { IonRefresher, NavController } from '@ionic/angular';
import { CoreCourses, CoreCoursesProvider } from '../../services/courses';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreSites } from '@services/sites';
+import { CoreCoursesDashboard } from '@features/courses/services/dashboard';
+import { CoreDomUtils } from '@services/utils/dom';
+import { CoreCourseBlock } from '@features/course/services/course';
+import { CoreBlockComponent } from '@features/block/components/block/block';
/**
* Page that displays the dashboard page.
@@ -29,16 +33,20 @@ import { CoreSites } from '@services/sites';
})
export class CoreCoursesDashboardPage implements OnInit, OnDestroy {
+ @ViewChildren(CoreBlockComponent) blocksComponents?: QueryList;
+
+
searchEnabled = false;
downloadEnabled = false;
downloadCourseEnabled = false;
downloadCoursesEnabled = false;
downloadEnabledIcon = 'far-square';
+ userId?: number;
+ blocks: Partial[] = [];
+ loaded = false;
protected updateSiteObserver?: CoreEventObserver;
- siteName = 'Hello world';
-
constructor(
protected navCtrl: NavController,
) { }
@@ -59,8 +67,82 @@ export class CoreCoursesDashboardPage implements OnInit, OnDestroy {
this.switchDownload(this.downloadEnabled && this.downloadCourseEnabled && this.downloadCoursesEnabled);
}, CoreSites.instance.getCurrentSiteId());
+
+ this.loadContent();
}
+ /**
+ * Convenience function to fetch the dashboard data.
+ *
+ * @return Promise resolved when done.
+ */
+ protected async loadContent(): Promise {
+ const available = await CoreCoursesDashboard.instance.isAvailable();
+
+ if (available) {
+ this.userId = CoreSites.instance.getCurrentSiteUserId();
+
+ try {
+ this.blocks = await CoreCoursesDashboard.instance.getDashboardBlocks();
+ } catch (error) {
+ CoreDomUtils.instance.showErrorModal(error);
+
+ // Cannot get the blocks, just show dashboard if needed.
+ this.loadFallbackBlocks();
+ }
+ } else if (!CoreCoursesDashboard.instance.isDisabledInSite()) {
+ // Not available, but not disabled either. Use fallback.
+ this.loadFallbackBlocks();
+ } else {
+ // Disabled.
+ this.blocks = [];
+ }
+
+ // this.dashboardEnabled = this.blockDelegate.hasSupportedBlock(this.blocks);
+ this.loaded = true;
+ }
+
+ /**
+ * Load fallback blocks to shown before 3.6 when dashboard blocks are not supported.
+ */
+ protected loadFallbackBlocks(): void {
+ this.blocks = [
+ {
+ name: 'myoverview',
+ visible: true,
+ },
+ {
+ name: 'timeline',
+ visible: true,
+ },
+ ];
+ }
+
+ /**
+ * Refresh the dashboard data.
+ *
+ * @param refresher Refresher.
+ */
+ refreshDashboard(refresher: CustomEvent): void {
+ const promises: Promise[] = [];
+
+ promises.push(CoreCoursesDashboard.instance.invalidateDashboardBlocks());
+
+ // Invalidate the blocks.
+ this.blocksComponents?.forEach((blockComponent) => {
+ promises.push(blockComponent.invalidate().catch(() => {
+ // Ignore errors.
+ }));
+ });
+
+ Promise.all(promises).finally(() => {
+ this.loadContent().finally(() => {
+ refresher?.detail.complete();
+ });
+ });
+ }
+
+
/**
* Toggle download enabled.
*/
diff --git a/src/core/features/courses/services/dashboard.ts b/src/core/features/courses/services/dashboard.ts
new file mode 100644
index 000000000..fbf19c3d7
--- /dev/null
+++ b/src/core/features/courses/services/dashboard.ts
@@ -0,0 +1,140 @@
+// (C) Copyright 2015 Moodle Pty Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Injectable } from '@angular/core';
+import { CoreSites } from '@services/sites';
+import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
+import { CoreCourseBlock } from '@features/course/services/course';
+import { CoreStatusWithWarningsWSResponse } from '@services/ws';
+import { makeSingleton } from '@singletons';
+
+const ROOT_CACHE_KEY = 'CoreCoursesDashboard:';
+
+/**
+ * Service that provides some features regarding course overview.
+ */
+@Injectable({ providedIn: 'root' })
+export class CoreCoursesDashboardProvider {
+
+ /**
+ * Get cache key for dashboard blocks WS calls.
+ *
+ * @param userId User ID. Default, 0 means current user.
+ * @return Cache key.
+ */
+ protected getDashboardBlocksCacheKey(userId: number = 0): string {
+ return ROOT_CACHE_KEY + 'blocks:' + userId;
+ }
+
+ /**
+ * Get dashboard blocks.
+ *
+ * @param userId User ID. Default, current user.
+ * @param siteId Site ID. If not defined, current site.
+ * @return Promise resolved with the list of blocks.
+ * @since 3.6
+ */
+ async getDashboardBlocks(userId?: number, siteId?: string): Promise {
+ const site = await CoreSites.instance.getSite(siteId);
+
+ const params: CoreBlockGetDashboardBlocksWSParams = {
+ returncontents: true,
+ };
+ const preSets: CoreSiteWSPreSets = {
+ cacheKey: this.getDashboardBlocksCacheKey(userId),
+ updateFrequency: CoreSite.FREQUENCY_RARELY,
+ };
+ if (userId) {
+ params.userid = userId;
+ }
+ const result = await site.read('core_block_get_dashboard_blocks', params, preSets);
+
+ return result.blocks || [];
+ }
+
+ /**
+ * Invalidates dashboard blocks WS call.
+ *
+ * @param userId User ID. Default, current user.
+ * @param siteId Site ID. If not defined, current site.
+ * @return Promise resolved when the data is invalidated.
+ */
+ async invalidateDashboardBlocks(userId?: number, siteId?: string): Promise {
+ const site = await CoreSites.instance.getSite(siteId);
+
+ return await site.invalidateWsCacheForKey(this.getDashboardBlocksCacheKey(userId));
+ }
+
+ /**
+ * Returns whether or not block based Dashboard is available for a certain site.
+ *
+ * @param siteId Site ID. If not defined, current site.
+ * @return Promise resolved with true if available, resolved with false or rejected otherwise.
+ * @since 3.6
+ */
+ async isAvailable(siteId?: string): Promise {
+ const site = await CoreSites.instance.getSite(siteId);
+
+ // First check if it's disabled.
+ if (this.isDisabledInSite(site)) {
+ return false;
+ }
+
+ return site.wsAvailable('core_block_get_dashboard_blocks');
+ }
+
+ /**
+ * Check if Site Home is disabled in a certain site.
+ *
+ * @param siteId Site Id. If not defined, use current site.
+ * @return Promise resolved with true if disabled, rejected or resolved with false otherwise.
+ */
+ async isDisabled(siteId?: string): Promise {
+ const site = await CoreSites.instance.getSite(siteId);
+
+ return this.isDisabledInSite(site);
+ }
+
+ /**
+ * Check if Site Home is disabled in a certain site.
+ *
+ * @param site Site. If not defined, use current site.
+ * @return Whether it's disabled.
+ */
+ isDisabledInSite(site?: CoreSite): boolean {
+ site = site || CoreSites.instance.getCurrentSite();
+
+ return !!site?.isFeatureDisabled('CoreMainMenuDelegate_CoreCoursesDashboard');
+ }
+
+}
+
+export class CoreCoursesDashboard extends makeSingleton(CoreCoursesDashboardProvider) {}
+
+
+/**
+ * Params of core_block_get_dashboard_blocks WS.
+ */
+type CoreBlockGetDashboardBlocksWSParams = {
+ userid?: number; // User id (optional), default is current user.
+ returncontents?: boolean; // Whether to return the block contents.
+};
+
+/**
+ * Data returned by core_block_get_dashboard_blocks WS.
+ */
+type CoreBlockGetDashboardBlocksWSResponse = {
+ blocks: CoreCourseBlock[]; // List of blocks in the course.
+ warnings?: CoreStatusWithWarningsWSResponse[];
+};
diff --git a/src/core/features/courses/services/handlers/dashboard-home.ts b/src/core/features/courses/services/handlers/dashboard-home.ts
index 0f2ce7e4e..e3a3edb1c 100644
--- a/src/core/features/courses/services/handlers/dashboard-home.ts
+++ b/src/core/features/courses/services/handlers/dashboard-home.ts
@@ -13,8 +13,10 @@
// limitations under the License.
import { Injectable } from '@angular/core';
+import { CoreBlockDelegate } from '@features/block/services/block-delegate';
import { CoreMainMenuHomeHandler, CoreMainMenuHomeHandlerToDisplay } from '@features/mainmenu/services/home-delegate';
import { makeSingleton } from '@singletons';
+import { CoreCoursesDashboard } from '../dashboard';
/**
* Handler to add dashboard into home page.
@@ -42,10 +44,10 @@ export class CoreDashboardHomeHandlerService implements CoreMainMenuHomeHandler
* @param siteId Site ID. If not defined, current site.
* @return Whether or not the handler is enabled on a site level.
*/
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
async isEnabledForSite(siteId?: string): Promise {
- // @todo return this.blockDelegate.hasSupportedBlock(this.blocks);
- return true;
+ const blocks = await CoreCoursesDashboard.instance.getDashboardBlocks(undefined, siteId);
+
+ return CoreBlockDelegate.instance.hasSupportedBlock(blocks);
}
/**
diff --git a/src/core/features/courses/services/handlers/my-courses.home.ts b/src/core/features/courses/services/handlers/my-courses-home.ts
similarity index 84%
rename from src/core/features/courses/services/handlers/my-courses.home.ts
rename to src/core/features/courses/services/handlers/my-courses-home.ts
index 49477e125..43796c803 100644
--- a/src/core/features/courses/services/handlers/my-courses.home.ts
+++ b/src/core/features/courses/services/handlers/my-courses-home.ts
@@ -13,8 +13,11 @@
// limitations under the License.
import { Injectable } from '@angular/core';
+import { CoreBlockDelegate } from '@features/block/services/block-delegate';
import { CoreMainMenuHomeHandler, CoreMainMenuHomeHandlerToDisplay } from '@features/mainmenu/services/home-delegate';
+import { CoreSiteHome } from '@features/sitehome/services/sitehome';
import { makeSingleton } from '@singletons';
+import { CoreCoursesDashboard } from '../dashboard';
/**
* Handler to add my courses into home page.
@@ -42,10 +45,10 @@ export class CoreCoursesMyCoursesHomeHandlerService implements CoreMainMenuHomeH
* @param siteId Site ID. If not defined, current site.
* @return Whether or not the handler is enabled on a site level.
*/
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
async isEnabledForSite(siteId?: string): Promise {
- // @todo return !this.blockDelegate.hasSupportedBlock(this.blocks) && !CoreSiteHome.instance.isAvailable(siteId);
- return true;
+ const blocks = await CoreCoursesDashboard.instance.getDashboardBlocks(undefined, siteId);
+
+ return !CoreBlockDelegate.instance.hasSupportedBlock(blocks)&& !CoreSiteHome.instance.isAvailable(siteId);
}
/**
diff --git a/src/core/features/sitehome/pages/index/index.ts b/src/core/features/sitehome/pages/index/index.ts
index bf62e8373..6ad4cc726 100644
--- a/src/core/features/sitehome/pages/index/index.ts
+++ b/src/core/features/sitehome/pages/index/index.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { Component, OnDestroy, OnInit } from '@angular/core';
+import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { IonRefresher, NavController } from '@ionic/angular';
@@ -24,6 +24,7 @@ import { CoreSiteHome } from '@features/sitehome/services/sitehome';
import { CoreCourses, CoreCoursesProvider } from '@features//courses/services/courses';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreCourseHelper } from '@features/course/services/course-helper';
+import { CoreBlockCourseBlocksComponent } from '@features/block/components/course-blocks/course-blocks';
/**
* Page that displays site home index.
@@ -34,7 +35,7 @@ import { CoreCourseHelper } from '@features/course/services/course-helper';
})
export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
- // @todo @ViewChild(CoreBlockCourseBlocksComponent) courseBlocksComponent: CoreBlockCourseBlocksComponent;
+ @ViewChild(CoreBlockCourseBlocksComponent) courseBlocksComponent?: CoreBlockCourseBlocksComponent;
dataLoaded = false;
section?: CoreCourseSection & {
@@ -158,13 +159,17 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
// @todo promises.push(this.prefetchDelegate.invalidateModules(this.section.modules, this.siteHomeId));
}
- // @todo promises.push(this.courseBlocksComponent.invalidateBlocks());
+ if (this.courseBlocksComponent) {
+ promises.push(this.courseBlocksComponent.invalidateBlocks());
+ }
Promise.all(promises).finally(async () => {
const p2: Promise[] = [];
p2.push(this.loadContent());
- // @todo p2.push(this.courseBlocksComponent.loadContent());
+ if (this.courseBlocksComponent) {
+ p2.push(this.courseBlocksComponent.loadContent());
+ }
await Promise.all(p2).finally(() => {
refresher?.detail.complete();