From 9a5f56738746b142678e7355451fa822758d5619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Mon, 6 May 2019 10:51:45 +0200 Subject: [PATCH] MOBILE-3014 course: Add course blocks tab --- scripts/langindex.json | 1 + src/assets/lang/en.json | 1 + src/core/block/block.module.ts | 14 ++- .../block/components/components.module.ts | 10 +- .../core-block-course-blocks.html | 15 +++ .../components/course-blocks/course-blocks.ts | 100 ++++++++++++++++++ src/core/block/lang/en.json | 3 + .../block/providers/course-option-handler.ts | 88 +++++++++++++++ 8 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 src/core/block/components/course-blocks/core-block-course-blocks.html create mode 100644 src/core/block/components/course-blocks/course-blocks.ts create mode 100644 src/core/block/lang/en.json create mode 100644 src/core/block/providers/course-option-handler.ts diff --git a/scripts/langindex.json b/scripts/langindex.json index 7381c6fc8..5abbddcb4 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -1231,6 +1231,7 @@ "core.answered": "quiz", "core.areyousure": "moodle", "core.back": "moodle", + "core.block.blocks": "moodle", "core.cancel": "moodle", "core.cannotconnect": "local_moodlemobileapp", "core.cannotdownloadfiles": "local_moodlemobileapp", diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index 7ed0d4812..7608fe277 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -1231,6 +1231,7 @@ "core.answered": "Answered", "core.areyousure": "Are you sure?", "core.back": "Back", + "core.block.blocks": "Blocks", "core.cancel": "Cancel", "core.cannotconnect": "Cannot connect: Verify that you have correctly typed the URL and that your site uses Moodle 2.4 or later.", "core.cannotdownloadfiles": "File downloading is disabled. Please contact your site administrator.", diff --git a/src/core/block/block.module.ts b/src/core/block/block.module.ts index 2448c6e1d..764d27fb9 100644 --- a/src/core/block/block.module.ts +++ b/src/core/block/block.module.ts @@ -15,6 +15,9 @@ import { NgModule } from '@angular/core'; import { CoreBlockDelegate } from './providers/delegate'; import { CoreBlockDefaultHandler } from './providers/default-block-handler'; +import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate'; +import { CoreBlockCourseBlocksCourseOptionHandler } from './providers/course-option-handler'; +import { CoreBlockComponentsModule } from './components/components.module'; // List of providers (without handlers). export const CORE_BLOCK_PROVIDERS: any[] = [ @@ -24,11 +27,18 @@ export const CORE_BLOCK_PROVIDERS: any[] = [ @NgModule({ declarations: [], imports: [ + CoreBlockComponentsModule ], providers: [ CoreBlockDelegate, - CoreBlockDefaultHandler + CoreBlockDefaultHandler, + CoreBlockCourseBlocksCourseOptionHandler ], exports: [] }) -export class CoreBlockModule {} +export class CoreBlockModule { + constructor(courseOptionHandler: CoreBlockCourseBlocksCourseOptionHandler, + courseOptionsDelegate: CoreCourseOptionsDelegate) { + courseOptionsDelegate.registerHandler(courseOptionHandler); + } +} diff --git a/src/core/block/components/components.module.ts b/src/core/block/components/components.module.ts index 896804473..33c80abcf 100644 --- a/src/core/block/components/components.module.ts +++ b/src/core/block/components/components.module.ts @@ -19,12 +19,14 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreDirectivesModule } from '@directives/directives.module'; import { CoreBlockComponent } from './block/block'; import { CoreBlockOnlyTitleComponent } from './only-title-block/only-title-block'; +import { CoreBlockCourseBlocksComponent } from './course-blocks/course-blocks'; import { CoreComponentsModule } from '@components/components.module'; @NgModule({ declarations: [ CoreBlockComponent, - CoreBlockOnlyTitleComponent + CoreBlockOnlyTitleComponent, + CoreBlockCourseBlocksComponent ], imports: [ CommonModule, @@ -37,10 +39,12 @@ import { CoreComponentsModule } from '@components/components.module'; ], exports: [ CoreBlockComponent, - CoreBlockOnlyTitleComponent + CoreBlockOnlyTitleComponent, + CoreBlockCourseBlocksComponent ], entryComponents: [ - CoreBlockOnlyTitleComponent + CoreBlockOnlyTitleComponent, + CoreBlockCourseBlocksComponent ] }) export class CoreBlockComponentsModule {} diff --git a/src/core/block/components/course-blocks/core-block-course-blocks.html b/src/core/block/components/course-blocks/core-block-course-blocks.html new file mode 100644 index 000000000..cf9457d58 --- /dev/null +++ b/src/core/block/components/course-blocks/core-block-course-blocks.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/core/block/components/course-blocks/course-blocks.ts b/src/core/block/components/course-blocks/course-blocks.ts new file mode 100644 index 000000000..8adbb5e4e --- /dev/null +++ b/src/core/block/components/course-blocks/course-blocks.ts @@ -0,0 +1,100 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Component, ViewChildren, Input, OnInit, QueryList } from '@angular/core'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreBlockComponent } from '../block/block'; +import { CoreBlockDelegate } from '../../providers/delegate'; +import { CoreCourseProvider } from '@core/course/providers/course'; + +/** + * Component that displays the list of course blocks. + */ +@Component({ + selector: 'core-block-course-blocks', + templateUrl: 'core-block-course-blocks.html', +}) +export class CoreBlockCourseBlocksComponent implements OnInit { + + @Input() courseId: number; + + @ViewChildren(CoreBlockComponent) blocksComponents: QueryList; + + dataLoaded = false; + hasContent: boolean; + hasSupportedBlock: boolean; + blocks = []; + + constructor(private domUtils: CoreDomUtilsProvider, private courseProvider: CoreCourseProvider, + private blockDelegate: CoreBlockDelegate) { + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.loadContent().finally(() => { + this.dataLoaded = true; + }); + } + + /** + * Refresh the data. + * + * @param {any} refresher Refresher. + */ + doRefresh(refresher: any): void { + const promises = []; + + if (this.courseProvider.canGetCourseBlocks()) { + promises.push(this.courseProvider.invalidateCourseBlocks(this.courseId)); + } + + // Invalidate the blocks. + this.blocksComponents.forEach((blockComponent) => { + promises.push(blockComponent.invalidate().catch(() => { + // Ignore errors. + })); + }); + + Promise.all(promises).finally(() => { + this.loadContent().finally(() => { + refresher.complete(); + }); + }); + } + + /** + * Convenience function to fetch the data. + * + * @return {Promise} Promise resolved when done. + */ + protected loadContent(): Promise { + // Get site home blocks. + const canGetBlocks = this.courseProvider.canGetCourseBlocks(), + promise = canGetBlocks ? this.courseProvider.getCourseBlocks(this.courseId) : Promise.reject(null); + + return promise.then((blocks) => { + this.blocks = blocks; + this.hasSupportedBlock = this.blockDelegate.hasSupportedBlock(blocks); + + }).catch((error) => { + if (canGetBlocks) { + this.domUtils.showErrorModal(error); + } + this.blocks = []; + }); + + } +} diff --git a/src/core/block/lang/en.json b/src/core/block/lang/en.json new file mode 100644 index 000000000..9b136b8ee --- /dev/null +++ b/src/core/block/lang/en.json @@ -0,0 +1,3 @@ +{ + "blocks": "Blocks" +} \ No newline at end of file diff --git a/src/core/block/providers/course-option-handler.ts b/src/core/block/providers/course-option-handler.ts new file mode 100644 index 000000000..c7e298c9c --- /dev/null +++ b/src/core/block/providers/course-option-handler.ts @@ -0,0 +1,88 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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, Injector } from '@angular/core'; +import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '@core/course/providers/options-delegate'; +import { CoreCourseProvider } from '@core/course/providers/course'; +import { CoreBlockCourseBlocksComponent } from '../components/course-blocks/course-blocks'; + +/** + * Course nav handler. + */ +@Injectable() +export class CoreBlockCourseBlocksCourseOptionHandler implements CoreCourseOptionsHandler { + name = 'CoreCourseBlocks'; + priority = 700; + + constructor(private courseProvider: CoreCourseProvider) {} + + /** + * Should invalidate the data to determine if the handler is enabled for a certain course. + * + * @param {number} courseId The course ID. + * @param {any} [navOptions] Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. + * @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. + * @return {Promise} Promise resolved when done. + */ + invalidateEnabledForCourse(courseId: number, navOptions?: any, admOptions?: any): Promise { + return this.courseProvider.invalidateCourseBlocks(courseId); + } + + /** + * Check if the handler is enabled on a site level. + * + * @return {boolean} Whether or not the handler is enabled on a site level. + */ + isEnabled(): boolean | Promise { + return this.courseProvider.canGetCourseBlocks(); + } + + /** + * Whether or not the handler is enabled for a certain course. + * + * @param {number} courseId The course ID. + * @param {any} accessData Access type and data. Default, guest, ... + * @param {any} [navOptions] Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions. + * @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions. + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise { + return true; + } + + /** + * Returns the data needed to render the handler. + * + * @param {Injector} injector Injector. + * @param {number} courseId The course ID. + * @return {CoreCourseOptionsHandlerData|Promise} Data or promise resolved with the data. + */ + getDisplayData(injector: Injector, courseId: number): CoreCourseOptionsHandlerData | Promise { + return { + title: 'core.block.blocks', + class: 'core-course-blocks-handler', + component: CoreBlockCourseBlocksComponent + }; + } + + /** + * Called when a course is downloaded. It should prefetch all the data to be able to see the addon in offline. + * + * @param {any} course The course. + * @return {Promise} Promise resolved when done. + */ + prefetch(course: any): Promise { + return this.courseProvider.getCourseBlocks(course.id); + } +}