From 521d234def1e9608a7d49dd70c22a5127827e0bf Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 15 Mar 2018 10:23:09 +0100 Subject: [PATCH] MOBILE-2330 question: Implement question behaviour delegate --- .../question/providers/behaviour-delegate.ts | 112 ++++++++++++++++++ .../providers/default-behaviour-handler.ts | 66 +++++++++++ src/core/question/question.module.ts | 8 +- 3 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 src/core/question/providers/behaviour-delegate.ts create mode 100644 src/core/question/providers/default-behaviour-handler.ts diff --git a/src/core/question/providers/behaviour-delegate.ts b/src/core/question/providers/behaviour-delegate.ts new file mode 100644 index 000000000..3ec8e6249 --- /dev/null +++ b/src/core/question/providers/behaviour-delegate.ts @@ -0,0 +1,112 @@ +// (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 } from '@angular/core'; +import { CoreLoggerProvider } from '@providers/logger'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; +import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; +import { CoreQuestionState } from './question'; +import { CoreQuestionDelegate } from './delegate'; +import { CoreQuestionBehaviourDefaultHandler } from './default-behaviour-handler'; + +/** + * Interface that all question behaviour handlers must implement. + */ +export interface CoreQuestionBehaviourHandler extends CoreDelegateHandler { + /** + * Type of the behaviour the handler supports. E.g. 'adaptive'. + * @type {string} + */ + type: string; + + /** + * Determine a question new state based on its answer(s). + * + * @param {string} component Component the question belongs to. + * @param {number} attemptId Attempt ID the question belongs to. + * @param {any} question The question. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {CoreQuestionState|Promise} State (or promise resolved with state). + */ + determineNewState?(component: string, attemptId: number, question: any, siteId?: string) + : CoreQuestionState | Promise; + + /** + * Handle a question behaviour. + * If the behaviour requires a submit button, it should add it to question.behaviourButtons. + * If the behaviour requires to show some extra data, it should return the components to render it. + * + * @param {any} question The question. + * @return {any[]|Promise} Components (or promise resolved with components) to render some extra data in the question + * (e.g. certainty options). Don't return anything if no extra data is required. + */ + handleQuestion?(question: any): any[] | Promise; +} + +/** + * Delegate to register question behaviour handlers. + */ +@Injectable() +export class CoreQuestionBehaviourDelegate extends CoreDelegate { + + protected handlerNameProperty = 'type'; + + constructor(logger: CoreLoggerProvider, sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider, + protected questionDelegate: CoreQuestionDelegate, protected defaultHandler: CoreQuestionBehaviourDefaultHandler) { + super('CoreQuestionBehaviourDelegate', logger, sitesProvider, eventsProvider); + } + + /** + * Determine a question new state based on its answer(s). + * + * @param {string} component Component the question belongs to. + * @param {number} attemptId Attempt ID the question belongs to. + * @param {any} question The question. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with state. + */ + determineNewState(behaviour: string, component: string, attemptId: number, question: any, siteId?: string) + : Promise { + behaviour = this.questionDelegate.getBehaviourForQuestion(question, behaviour); + + return Promise.resolve(this.executeFunctionOnEnabled(behaviour, 'determineNewState', + [component, attemptId, question, siteId])); + } + + /** + * Handle a question behaviour. + * If the behaviour requires a submit button, it should add it to question.behaviourButtons. + * If the behaviour requires to show some extra data, it should return a directive to render it. + * + * @param {string} behaviour Default behaviour. + * @param {any} question The question. + * @return {Promise} Promise resolved with components to render some extra data in the question. + */ + handleQuestion(behaviour: string, question: any): Promise { + behaviour = this.questionDelegate.getBehaviourForQuestion(question, behaviour); + + return Promise.resolve(this.executeFunctionOnEnabled(behaviour, 'handleQuestion', [question])); + } + + /** + * Check if a question behaviour is supported. + * + * @param {string} behaviour Name of the behaviour. + * @return {boolean} Whether it's supported. + */ + isBehaviourSupported(behaviour: string): boolean { + return this.hasHandler(behaviour, true); + } +} diff --git a/src/core/question/providers/default-behaviour-handler.ts b/src/core/question/providers/default-behaviour-handler.ts new file mode 100644 index 000000000..1eb790924 --- /dev/null +++ b/src/core/question/providers/default-behaviour-handler.ts @@ -0,0 +1,66 @@ +// (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 } from '@angular/core'; +import { CoreQuestionBehaviourHandler } from './behaviour-delegate'; +import { CoreQuestionProvider, CoreQuestionState } from '@core/question/providers/question'; + +/** + * Default handler used when the question behaviour doesn't have a specific implementation. + */ +@Injectable() +export class CoreQuestionBehaviourDefaultHandler implements CoreQuestionBehaviourHandler { + name = 'CoreQuestionBehaviourDefault'; + type = 'default'; + + constructor(private questionProvider: CoreQuestionProvider) { } + + /** + * Determine a question new state based on its answer(s). + * + * @param {string} component Component the question belongs to. + * @param {number} attemptId Attempt ID the question belongs to. + * @param {any} question The question. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {CoreQuestionState|Promise} New state (or promise resolved with state). + */ + determineNewState(component: string, attemptId: number, question: any, siteId?: string) + : CoreQuestionState | Promise { + // Return the current state. + return this.questionProvider.getState(question.state); + } + + /** + * Handle a question behaviour. + * If the behaviour requires a submit button, it should add it to question.behaviourButtons. + * If the behaviour requires to show some extra data, it should return the components to render it. + * + * @param {any} question The question. + * @return {any[]|Promise} Components (or promise resolved with components) to render some extra data in the question + * (e.g. certainty options). Don't return anything if no extra data is required. + */ + handleQuestion(question: any): any[] | Promise { + // Nothing to do. + return; + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean | Promise { + return true; + } +} diff --git a/src/core/question/question.module.ts b/src/core/question/question.module.ts index 23f9451b9..effaf5153 100644 --- a/src/core/question/question.module.ts +++ b/src/core/question/question.module.ts @@ -15,12 +15,15 @@ import { NgModule } from '@angular/core'; import { CoreQuestionProvider } from './providers/question'; import { CoreQuestionDelegate } from './providers/delegate'; +import { CoreQuestionBehaviourDelegate } from './providers/behaviour-delegate'; import { CoreQuestionDefaultHandler } from './providers/default-question-handler'; +import { CoreQuestionBehaviourDefaultHandler } from './providers/default-behaviour-handler'; // List of providers (without handlers). export const CORE_QUESTION_PROVIDERS: any[] = [ CoreQuestionProvider, - CoreQuestionDelegate + CoreQuestionDelegate, + CoreQuestionBehaviourDelegate ]; @NgModule({ @@ -28,7 +31,8 @@ export const CORE_QUESTION_PROVIDERS: any[] = [ imports: [ ], providers: CORE_QUESTION_PROVIDERS.concat([ - CoreQuestionDefaultHandler + CoreQuestionDefaultHandler, + CoreQuestionBehaviourDefaultHandler ]), exports: [] })