MOBILE-3014 course: Add course blocks tab
parent
26a1fad8c8
commit
9a5f567387
|
@ -1231,6 +1231,7 @@
|
||||||
"core.answered": "quiz",
|
"core.answered": "quiz",
|
||||||
"core.areyousure": "moodle",
|
"core.areyousure": "moodle",
|
||||||
"core.back": "moodle",
|
"core.back": "moodle",
|
||||||
|
"core.block.blocks": "moodle",
|
||||||
"core.cancel": "moodle",
|
"core.cancel": "moodle",
|
||||||
"core.cannotconnect": "local_moodlemobileapp",
|
"core.cannotconnect": "local_moodlemobileapp",
|
||||||
"core.cannotdownloadfiles": "local_moodlemobileapp",
|
"core.cannotdownloadfiles": "local_moodlemobileapp",
|
||||||
|
|
|
@ -1231,6 +1231,7 @@
|
||||||
"core.answered": "Answered",
|
"core.answered": "Answered",
|
||||||
"core.areyousure": "Are you sure?",
|
"core.areyousure": "Are you sure?",
|
||||||
"core.back": "Back",
|
"core.back": "Back",
|
||||||
|
"core.block.blocks": "Blocks",
|
||||||
"core.cancel": "Cancel",
|
"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.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.",
|
"core.cannotdownloadfiles": "File downloading is disabled. Please contact your site administrator.",
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CoreBlockDelegate } from './providers/delegate';
|
import { CoreBlockDelegate } from './providers/delegate';
|
||||||
import { CoreBlockDefaultHandler } from './providers/default-block-handler';
|
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).
|
// List of providers (without handlers).
|
||||||
export const CORE_BLOCK_PROVIDERS: any[] = [
|
export const CORE_BLOCK_PROVIDERS: any[] = [
|
||||||
|
@ -24,11 +27,18 @@ export const CORE_BLOCK_PROVIDERS: any[] = [
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [],
|
declarations: [],
|
||||||
imports: [
|
imports: [
|
||||||
|
CoreBlockComponentsModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
CoreBlockDelegate,
|
CoreBlockDelegate,
|
||||||
CoreBlockDefaultHandler
|
CoreBlockDefaultHandler,
|
||||||
|
CoreBlockCourseBlocksCourseOptionHandler
|
||||||
],
|
],
|
||||||
exports: []
|
exports: []
|
||||||
})
|
})
|
||||||
export class CoreBlockModule {}
|
export class CoreBlockModule {
|
||||||
|
constructor(courseOptionHandler: CoreBlockCourseBlocksCourseOptionHandler,
|
||||||
|
courseOptionsDelegate: CoreCourseOptionsDelegate) {
|
||||||
|
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,12 +19,14 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { CoreBlockComponent } from './block/block';
|
import { CoreBlockComponent } from './block/block';
|
||||||
import { CoreBlockOnlyTitleComponent } from './only-title-block/only-title-block';
|
import { CoreBlockOnlyTitleComponent } from './only-title-block/only-title-block';
|
||||||
|
import { CoreBlockCourseBlocksComponent } from './course-blocks/course-blocks';
|
||||||
import { CoreComponentsModule } from '@components/components.module';
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
CoreBlockComponent,
|
CoreBlockComponent,
|
||||||
CoreBlockOnlyTitleComponent
|
CoreBlockOnlyTitleComponent,
|
||||||
|
CoreBlockCourseBlocksComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
@ -37,10 +39,12 @@ import { CoreComponentsModule } from '@components/components.module';
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
CoreBlockComponent,
|
CoreBlockComponent,
|
||||||
CoreBlockOnlyTitleComponent
|
CoreBlockOnlyTitleComponent,
|
||||||
|
CoreBlockCourseBlocksComponent
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
CoreBlockOnlyTitleComponent
|
CoreBlockOnlyTitleComponent,
|
||||||
|
CoreBlockCourseBlocksComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CoreBlockComponentsModule {}
|
export class CoreBlockComponentsModule {}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="dataLoaded" (ionRefresh)="doRefresh($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="dataLoaded" class="core-loading-center">
|
||||||
|
<ion-list *ngIf="hasSupportedBlock">
|
||||||
|
<!-- Course blocks. -->
|
||||||
|
<ng-container *ngFor="let block of blocks">
|
||||||
|
<core-block [block]="block" contextLevel="course" [instanceId]="courseId"></core-block>
|
||||||
|
</ng-container>
|
||||||
|
</ion-list>
|
||||||
|
|
||||||
|
<core-empty-box *ngIf="!hasSupportedBlock" icon="qr-scanner" [message]="'core.course.nocontentavailable' | translate"></core-empty-box>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -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<CoreBlockComponent>;
|
||||||
|
|
||||||
|
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<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected loadContent(): Promise<any> {
|
||||||
|
// 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 = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"blocks": "Blocks"
|
||||||
|
}
|
|
@ -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<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
invalidateEnabledForCourse(courseId: number, navOptions?: any, admOptions?: any): Promise<any> {
|
||||||
|
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<boolean> {
|
||||||
|
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<boolean>} True or promise resolved with true if enabled.
|
||||||
|
*/
|
||||||
|
isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data needed to render the handler.
|
||||||
|
*
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @param {number} courseId The course ID.
|
||||||
|
* @return {CoreCourseOptionsHandlerData|Promise<CoreCourseOptionsHandlerData>} Data or promise resolved with the data.
|
||||||
|
*/
|
||||||
|
getDisplayData(injector: Injector, courseId: number): CoreCourseOptionsHandlerData | Promise<CoreCourseOptionsHandlerData> {
|
||||||
|
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<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
prefetch(course: any): Promise<any> {
|
||||||
|
return this.courseProvider.getCourseBlocks(course.id);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue