MOBILE-2376 siteplugins: Support question behaviours
parent
02fd27d3d4
commit
bf658df6b4
|
@ -13,7 +13,7 @@
|
|||
// 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 { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
|
||||
|
@ -34,11 +34,12 @@ export class AddonQbehaviourAdaptiveHandler implements CoreQuestionBehaviourHand
|
|||
* 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 {Injector} injector Injector.
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} 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<any[]> {
|
||||
handleQuestion(injector: Injector, question: any): any[] | Promise<any[]> {
|
||||
// Just extract the button, it doesn't need any specific component.
|
||||
this.questionHelper.extractQbehaviourButtons(question);
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// 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 { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
|
||||
|
@ -34,11 +34,12 @@ export class AddonQbehaviourAdaptiveNoPenaltyHandler implements CoreQuestionBeha
|
|||
* 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 {Injector} injector Injector.
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} 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<any[]> {
|
||||
handleQuestion(injector: Injector, question: any): any[] | Promise<any[]> {
|
||||
// Just extract the button, it doesn't need any specific component.
|
||||
this.questionHelper.extractQbehaviourButtons(question);
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// 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 { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionDelegate } from '@core/question/providers/delegate';
|
||||
import { CoreQuestionState } from '@core/question/providers/question';
|
||||
|
@ -55,11 +55,12 @@ export class AddonQbehaviourDeferredCBMHandler implements CoreQuestionBehaviourH
|
|||
* 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 {Injector} injector Injector.
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} 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<any[]> {
|
||||
handleQuestion(injector: Injector, question: any): any[] | Promise<any[]> {
|
||||
if (this.questionHelper.extractQbehaviourCBM(question)) {
|
||||
return [AddonQbehaviourDeferredCBMComponent];
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// 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 { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
import { AddonQbehaviourDeferredCBMComponent } from '@addon/qbehaviour/deferredcbm/component/deferredcbm';
|
||||
|
@ -35,11 +35,12 @@ export class AddonQbehaviourImmediateCBMHandler implements CoreQuestionBehaviour
|
|||
* 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 {Injector} injector Injector.
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} 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<any[]> {
|
||||
handleQuestion(injector: Injector, question: any): any[] | Promise<any[]> {
|
||||
// Just extract the button, it doesn't need any specific component.
|
||||
this.questionHelper.extractQbehaviourButtons(question);
|
||||
if (this.questionHelper.extractQbehaviourCBM(question)) {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// 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 { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
|
||||
|
@ -34,11 +34,12 @@ export class AddonQbehaviourImmediateFeedbackHandler implements CoreQuestionBeha
|
|||
* 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 {Injector} injector Injector.
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} 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<any[]> {
|
||||
handleQuestion(injector: Injector, question: any): any[] | Promise<any[]> {
|
||||
// Just extract the button, it doesn't need any specific component.
|
||||
this.questionHelper.extractQbehaviourButtons(question);
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// 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 { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionProvider, CoreQuestionState } from '@core/question/providers/question';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
|
@ -52,11 +52,12 @@ export class AddonQbehaviourInformationItemHandler implements CoreQuestionBehavi
|
|||
* 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 {Injector} injector Injector.
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} 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<any[]> {
|
||||
handleQuestion(injector: Injector, question: any): any[] | Promise<any[]> {
|
||||
if (this.questionHelper.extractQbehaviourSeenInput(question)) {
|
||||
return [AddonQbehaviourInformationItemComponent];
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// 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 { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
|
||||
|
@ -34,11 +34,12 @@ export class AddonQbehaviourInteractiveHandler implements CoreQuestionBehaviourH
|
|||
* 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 {Injector} injector Injector.
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} 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<any[]> {
|
||||
handleQuestion(injector: Injector, question: any): any[] | Promise<any[]> {
|
||||
// Just extract the button, it doesn't need any specific component.
|
||||
this.questionHelper.extractQbehaviourButtons(question);
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// 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 { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
|
||||
|
@ -34,11 +34,12 @@ export class AddonQbehaviourInteractiveCountbackHandler implements CoreQuestionB
|
|||
* 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 {Injector} injector Injector.
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} 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<any[]> {
|
||||
handleQuestion(injector: Injector, question: any): any[] | Promise<any[]> {
|
||||
// Just extract the button, it doesn't need any specific component.
|
||||
this.questionHelper.extractQbehaviourButtons(question);
|
||||
|
||||
|
|
|
@ -14,11 +14,10 @@
|
|||
|
||||
import {
|
||||
Component, Input, OnInit, OnChanges, OnDestroy, ViewContainerRef, ViewChild, ComponentRef, SimpleChange, ChangeDetectorRef,
|
||||
ElementRef, Optional
|
||||
ElementRef, Optional, Output, EventEmitter
|
||||
} from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
import { CoreCompileProvider } from '../../providers/compile';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
/**
|
||||
* This component has a behaviour similar to $compile for AngularJS. Given an HTML code, it will compile it so all its
|
||||
|
@ -43,18 +42,17 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy {
|
|||
@Input() text: string; // The HTML text to display.
|
||||
@Input() javascript: string; // The Javascript to execute in the component.
|
||||
@Input() jsData: any; // Data to pass to the fake component.
|
||||
@Output() created: EventEmitter<any> = new EventEmitter(); // Will emit an event when the component is instantiated.
|
||||
|
||||
// Get the container where to put the content.
|
||||
@ViewChild('dynamicComponent', { read: ViewContainerRef }) container: ViewContainerRef;
|
||||
|
||||
protected componentRef: ComponentRef<any>;
|
||||
protected element;
|
||||
componentObservable: BehaviorSubject<any>; // An observable to notify observers when the component is instantiated.
|
||||
|
||||
constructor(protected compileProvider: CoreCompileProvider, protected cdr: ChangeDetectorRef, element: ElementRef,
|
||||
@Optional() protected navCtrl: NavController) {
|
||||
this.element = element.nativeElement;
|
||||
this.componentObservable = new BehaviorSubject<any>(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,7 +68,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy {
|
|||
if (factory) {
|
||||
// Create the component.
|
||||
this.componentRef = this.container.createComponent(factory);
|
||||
this.componentObservable.next(this.componentRef.instance);
|
||||
this.created.emit(this.componentRef.instance);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -75,6 +75,9 @@ import { CoreCourseFormatSingleActivityComponent } from '@core/course/formats/si
|
|||
import { CoreSitePluginsModuleIndexComponent } from '@core/siteplugins/components/module-index/module-index';
|
||||
import { CoreSitePluginsCourseOptionComponent } from '@core/siteplugins/components/course-option/course-option';
|
||||
import { CoreSitePluginsCourseFormatComponent } from '@core/siteplugins/components/course-format/course-format';
|
||||
import { CoreSitePluginsQuestionComponent } from '@core/siteplugins/components/question/question';
|
||||
import { CoreSitePluginsQuestionBehaviourComponent } from '@core/siteplugins/components/question-behaviour/question-behaviour';
|
||||
import { CoreSitePluginsUserProfileFieldComponent } from '@core/siteplugins/components/user-profile-field/user-profile-field';
|
||||
|
||||
/**
|
||||
* Service to provide functionalities regarding compiling dynamic HTML and Javascript.
|
||||
|
@ -203,6 +206,9 @@ export class CoreCompileProvider {
|
|||
instance['CoreSitePluginsModuleIndexComponent'] = CoreSitePluginsModuleIndexComponent;
|
||||
instance['CoreSitePluginsCourseOptionComponent'] = CoreSitePluginsCourseOptionComponent;
|
||||
instance['CoreSitePluginsCourseFormatComponent'] = CoreSitePluginsCourseFormatComponent;
|
||||
instance['CoreSitePluginsQuestionComponent'] = CoreSitePluginsQuestionComponent;
|
||||
instance['CoreSitePluginsQuestionBehaviourComponent'] = CoreSitePluginsQuestionBehaviourComponent;
|
||||
instance['CoreSitePluginsUserProfileFieldComponent'] = CoreSitePluginsUserProfileFieldComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -123,11 +123,15 @@ export class CoreQuestionComponent implements OnInit {
|
|||
|
||||
promise.then(() => {
|
||||
// Handle behaviour.
|
||||
this.behaviourDelegate.handleQuestion(this.question.preferredBehaviour, this.question).then((comps) => {
|
||||
this.behaviourDelegate.handleQuestion(this.injector, this.question.preferredBehaviour, this.question)
|
||||
.then((comps) => {
|
||||
this.behaviourComponents = comps;
|
||||
});
|
||||
this.questionHelper.extractQbehaviourRedoButton(this.question);
|
||||
}).finally(() => {
|
||||
this.question.html = this.domUtils.removeElementFromHtml(this.question.html, '.im-controls');
|
||||
this.loaded = true;
|
||||
});
|
||||
|
||||
this.questionHelper.extractQbehaviourRedoButton(this.question);
|
||||
|
||||
// Extract the validation error of the question.
|
||||
this.question.validationError = this.questionHelper.getValidationErrorFromHtml(this.question.html);
|
||||
|
@ -138,8 +142,6 @@ export class CoreQuestionComponent implements OnInit {
|
|||
// Try to extract the feedback and comment for the question.
|
||||
this.questionHelper.extractQuestionFeedback(this.question);
|
||||
this.questionHelper.extractQuestionComment(this.question);
|
||||
|
||||
this.loaded = true;
|
||||
});
|
||||
}
|
||||
}).catch(() => {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// 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 { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
|
@ -48,11 +48,12 @@ export interface CoreQuestionBehaviourHandler extends CoreDelegateHandler {
|
|||
* 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 {Injector} injector Injector.
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} 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<any[]>;
|
||||
handleQuestion?(injector: Injector, question: any): any[] | Promise<any[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,14 +91,15 @@ export class CoreQuestionBehaviourDelegate extends CoreDelegate {
|
|||
* 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 {Injector} injector Injector.
|
||||
* @param {string} behaviour Default behaviour.
|
||||
* @param {any} question The question.
|
||||
* @return {Promise<any[]>} Promise resolved with components to render some extra data in the question.
|
||||
*/
|
||||
handleQuestion(behaviour: string, question: any): Promise<any[]> {
|
||||
handleQuestion(injector: Injector, behaviour: string, question: any): Promise<any[]> {
|
||||
behaviour = this.questionDelegate.getBehaviourForQuestion(question, behaviour);
|
||||
|
||||
return Promise.resolve(this.executeFunctionOnEnabled(behaviour, 'handleQuestion', [question]));
|
||||
return Promise.resolve(this.executeFunctionOnEnabled(behaviour, 'handleQuestion', [injector, question]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// 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 { CoreQuestionBehaviourHandler } from './behaviour-delegate';
|
||||
import { CoreQuestionProvider, CoreQuestionState } from '@core/question/providers/question';
|
||||
|
||||
|
@ -46,11 +46,12 @@ export class CoreQuestionBehaviourDefaultHandler implements CoreQuestionBehaviou
|
|||
* 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 {Injector} injector Injector.
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} 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<any[]> {
|
||||
handleQuestion(injector: Injector, question: any): any[] | Promise<any[]> {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
// (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 { CoreSitePluginsProvider } from '../providers/siteplugins';
|
||||
|
||||
/**
|
||||
* Base class for components that will display a component using core-compile-html and want to call a
|
||||
* componentInit function returned by the handler JS.
|
||||
*/
|
||||
export class CoreSitePluginsCompileInitComponent {
|
||||
content = ''; // Content.
|
||||
jsData: any; // Data to pass to the component.
|
||||
protected handlerSchema: any; // The handler data.
|
||||
|
||||
constructor(protected sitePluginsProvider: CoreSitePluginsProvider) { }
|
||||
|
||||
/**
|
||||
* Function called when the component is created.
|
||||
*
|
||||
* @param {any} instance The component instance.
|
||||
*/
|
||||
componentCreated(instance: any): void {
|
||||
// Check if the JS defined an init function.
|
||||
if (instance && this.handlerSchema && this.handlerSchema.methodJSResult &&
|
||||
this.handlerSchema.methodJSResult.componentInit) {
|
||||
this.handlerSchema.methodJSResult.componentInit.apply(instance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the handler data.
|
||||
*
|
||||
* @param {string} name The name of the handler.
|
||||
*/
|
||||
getHandlerData(name: string): void {
|
||||
// Retrieve the handler data.
|
||||
const handler = this.sitePluginsProvider.getSitePluginHandler(name);
|
||||
|
||||
this.handlerSchema = handler && handler.handlerSchema;
|
||||
|
||||
if (this.handlerSchema) {
|
||||
// Load first template.
|
||||
if (this.handlerSchema.methodTemplates && this.handlerSchema.methodTemplates.length) {
|
||||
this.content = handler.handlerSchema.methodTemplates[0].html;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// (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 { Injector } from '@angular/core';
|
||||
import { CoreQuestionBehaviourDefaultHandler } from '@core/question/providers/default-behaviour-handler';
|
||||
import { CoreSitePluginsQuestionBehaviourComponent } from '../components/question-behaviour/question-behaviour';
|
||||
import { CoreQuestionProvider } from '@core/question/providers/question';
|
||||
|
||||
/**
|
||||
* Handler to display a question behaviour site plugin.
|
||||
*/
|
||||
export class CoreSitePluginsQuestionBehaviourHandler extends CoreQuestionBehaviourDefaultHandler {
|
||||
|
||||
constructor(questionProvider: CoreQuestionProvider, public name: string, public type: string, public hasTemplate: boolean) {
|
||||
super(questionProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {Injector} injector Injector.
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} 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(injector: Injector, question: any): any[] | Promise<any[]> {
|
||||
if (this.hasTemplate) {
|
||||
return [CoreSitePluginsQuestionBehaviourComponent];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import { CoreSitePluginsCourseOptionComponent } from './course-option/course-opt
|
|||
import { CoreSitePluginsCourseFormatComponent } from './course-format/course-format';
|
||||
import { CoreSitePluginsUserProfileFieldComponent } from './user-profile-field/user-profile-field';
|
||||
import { CoreSitePluginsQuestionComponent } from './question/question';
|
||||
import { CoreSitePluginsQuestionBehaviourComponent } from './question-behaviour/question-behaviour';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -32,7 +33,8 @@ import { CoreSitePluginsQuestionComponent } from './question/question';
|
|||
CoreSitePluginsCourseOptionComponent,
|
||||
CoreSitePluginsCourseFormatComponent,
|
||||
CoreSitePluginsUserProfileFieldComponent,
|
||||
CoreSitePluginsQuestionComponent
|
||||
CoreSitePluginsQuestionComponent,
|
||||
CoreSitePluginsQuestionBehaviourComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
@ -49,14 +51,16 @@ import { CoreSitePluginsQuestionComponent } from './question/question';
|
|||
CoreSitePluginsCourseOptionComponent,
|
||||
CoreSitePluginsCourseFormatComponent,
|
||||
CoreSitePluginsUserProfileFieldComponent,
|
||||
CoreSitePluginsQuestionComponent
|
||||
CoreSitePluginsQuestionComponent,
|
||||
CoreSitePluginsQuestionBehaviourComponent
|
||||
],
|
||||
entryComponents: [
|
||||
CoreSitePluginsModuleIndexComponent,
|
||||
CoreSitePluginsCourseOptionComponent,
|
||||
CoreSitePluginsCourseFormatComponent,
|
||||
CoreSitePluginsUserProfileFieldComponent,
|
||||
CoreSitePluginsQuestionComponent
|
||||
CoreSitePluginsQuestionComponent,
|
||||
CoreSitePluginsQuestionBehaviourComponent
|
||||
]
|
||||
})
|
||||
export class CoreSitePluginsComponentsModule {}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<core-compile-html [text]="content" [jsData]="jsData" (created)="componentCreated($event)"></core-compile-html>
|
|
@ -0,0 +1,58 @@
|
|||
// (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, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { CoreSitePluginsProvider } from '../../providers/siteplugins';
|
||||
import { CoreSitePluginsCompileInitComponent } from '../../classes/compile-init-component';
|
||||
|
||||
/**
|
||||
* Component that displays a question behaviour created using a site plugin.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-site-plugins-question-behaviour',
|
||||
templateUrl: 'question-behaviour.html',
|
||||
})
|
||||
export class CoreSitePluginsQuestionBehaviourComponent extends CoreSitePluginsCompileInitComponent implements OnInit {
|
||||
@Input() question: any; // The question where the behaviour will be rendered.
|
||||
@Input() component: string; // The component the question belongs to.
|
||||
@Input() componentId: number; // ID of the component the question belongs to.
|
||||
@Input() attemptId: number; // Attempt ID.
|
||||
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
|
||||
@Output() buttonClicked: EventEmitter<any>; // Should emit an event when a behaviour button is clicked.
|
||||
@Output() onAbort: EventEmitter<void>; // Should emit an event if the question should be aborted.
|
||||
|
||||
constructor(sitePluginsProvider: CoreSitePluginsProvider) {
|
||||
super(sitePluginsProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
// Pass the input and output data to the component.
|
||||
this.jsData = {
|
||||
question: this.question,
|
||||
component: this.component,
|
||||
componentId: this.componentId,
|
||||
attemptId: this.attemptId,
|
||||
offlineEnabled: this.offlineEnabled,
|
||||
buttonClicked: this.buttonClicked,
|
||||
onAbort: this.onAbort
|
||||
};
|
||||
|
||||
if (this.question) {
|
||||
this.getHandlerData('qbehaviour_' + this.question.preferredBehaviour);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
<core-compile-html [text]="content" [jsData]="jsData"></core-compile-html>
|
||||
<core-compile-html [text]="content" [jsData]="jsData" (created)="componentCreated($event)"></core-compile-html>
|
||||
|
|
|
@ -12,10 +12,9 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, Input, Output, EventEmitter, ViewChild, OnDestroy } from '@angular/core';
|
||||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { CoreSitePluginsProvider } from '../../providers/siteplugins';
|
||||
import { CoreCompileHtmlComponent } from '@core/compile/components/compile-html/compile-html';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { CoreSitePluginsCompileInitComponent } from '../../classes/compile-init-component';
|
||||
|
||||
/**
|
||||
* Component that displays a question created using a site plugin.
|
||||
|
@ -24,7 +23,7 @@ import { Subscription } from 'rxjs';
|
|||
selector: 'core-site-plugins-question',
|
||||
templateUrl: 'question.html',
|
||||
})
|
||||
export class CoreSitePluginsQuestionComponent implements OnInit, OnDestroy {
|
||||
export class CoreSitePluginsQuestionComponent extends CoreSitePluginsCompileInitComponent implements OnInit {
|
||||
@Input() question: any; // The question to render.
|
||||
@Input() component: string; // The component the question belongs to.
|
||||
@Input() componentId: number; // ID of the component the question belongs to.
|
||||
|
@ -33,13 +32,9 @@ export class CoreSitePluginsQuestionComponent implements OnInit, OnDestroy {
|
|||
@Output() buttonClicked: EventEmitter<any>; // Should emit an event when a behaviour button is clicked.
|
||||
@Output() onAbort: EventEmitter<void>; // Should emit an event if the question should be aborted.
|
||||
|
||||
@ViewChild(CoreCompileHtmlComponent) compileComponent: CoreCompileHtmlComponent;
|
||||
|
||||
content = ''; // Content.
|
||||
jsData;
|
||||
protected componentObserver: Subscription;
|
||||
|
||||
constructor(protected sitePluginsProvider: CoreSitePluginsProvider) { }
|
||||
constructor(sitePluginsProvider: CoreSitePluginsProvider) {
|
||||
super(sitePluginsProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
|
@ -57,34 +52,7 @@ export class CoreSitePluginsQuestionComponent implements OnInit, OnDestroy {
|
|||
};
|
||||
|
||||
if (this.question) {
|
||||
// Retrieve the handler data.
|
||||
const handler = this.sitePluginsProvider.getSitePluginHandler('qtype_' + this.question.type),
|
||||
handlerSchema = handler && handler.handlerSchema;
|
||||
|
||||
if (handlerSchema) {
|
||||
// Load first template.
|
||||
if (handlerSchema.methodTemplates && handlerSchema.methodTemplates.length) {
|
||||
this.content = handler.handlerSchema.methodTemplates[0].html;
|
||||
}
|
||||
|
||||
// Wait for the instance to be created.
|
||||
if (this.compileComponent && this.compileComponent.componentObservable &&
|
||||
handlerSchema.methodJSResult && handlerSchema.methodJSResult.componentInit) {
|
||||
this.componentObserver = this.compileComponent.componentObservable.subscribe((instance) => {
|
||||
if (instance) {
|
||||
// Instance created, call component init.
|
||||
handlerSchema.methodJSResult.componentInit.apply(instance);
|
||||
}
|
||||
});
|
||||
this.getHandlerData('qtype_' + this.question.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.componentObserver && this.componentObserver.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
<core-compile-html [text]="content" [jsData]="jsData"></core-compile-html>
|
||||
<core-compile-html [text]="content" [jsData]="jsData" (created)="componentCreated($event)"></core-compile-html>
|
||||
|
|
|
@ -12,10 +12,9 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, Input, ViewChild, OnDestroy } from '@angular/core';
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { CoreSitePluginsProvider } from '../../providers/siteplugins';
|
||||
import { CoreCompileHtmlComponent } from '@core/compile/components/compile-html/compile-html';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { CoreSitePluginsCompileInitComponent } from '../../classes/compile-init-component';
|
||||
|
||||
/**
|
||||
* Component that displays a user profile field created using a site plugin.
|
||||
|
@ -24,20 +23,16 @@ import { Subscription } from 'rxjs';
|
|||
selector: 'core-site-plugins-user-profile-field',
|
||||
templateUrl: 'user-profile-field.html',
|
||||
})
|
||||
export class CoreSitePluginsUserProfileFieldComponent implements OnInit, OnDestroy {
|
||||
export class CoreSitePluginsUserProfileFieldComponent extends CoreSitePluginsCompileInitComponent implements OnInit {
|
||||
@Input() field: any; // The profile field to be rendered.
|
||||
@Input() signup = false; // True if editing the field in signup. Defaults to false.
|
||||
@Input() edit = false; // True if editing the field. Defaults to false.
|
||||
@Input() form?: any; // Form where to add the form control. Required if edit=true or signup=true.
|
||||
@Input() registerAuth?: string; // Register auth method. E.g. 'email'.
|
||||
|
||||
@ViewChild(CoreCompileHtmlComponent) compileComponent: CoreCompileHtmlComponent;
|
||||
|
||||
content = ''; // Content.
|
||||
jsData;
|
||||
protected componentObserver: Subscription;
|
||||
|
||||
constructor(protected sitePluginsProvider: CoreSitePluginsProvider) { }
|
||||
constructor(sitePluginsProvider: CoreSitePluginsProvider) {
|
||||
super(sitePluginsProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
|
@ -54,34 +49,7 @@ export class CoreSitePluginsUserProfileFieldComponent implements OnInit, OnDestr
|
|||
};
|
||||
|
||||
if (this.field) {
|
||||
// Retrieve the handler data.
|
||||
const handler = this.sitePluginsProvider.getSitePluginHandler(this.field.type || this.field.datatype),
|
||||
handlerSchema = handler && handler.handlerSchema;
|
||||
|
||||
if (handlerSchema) {
|
||||
// Load first template.
|
||||
if (handlerSchema.methodTemplates && handlerSchema.methodTemplates.length) {
|
||||
this.content = handler.handlerSchema.methodTemplates[0].html;
|
||||
}
|
||||
|
||||
// Wait for the instance to be created.
|
||||
if (this.compileComponent && this.compileComponent.componentObservable &&
|
||||
handlerSchema.methodJSResult && handlerSchema.methodJSResult.componentInit) {
|
||||
this.componentObserver = this.compileComponent.componentObservable.subscribe((instance) => {
|
||||
if (instance) {
|
||||
// Instance created, call component init.
|
||||
handlerSchema.methodJSResult.componentInit.apply(instance);
|
||||
}
|
||||
});
|
||||
this.getHandlerData(this.field.type || this.field.datatype);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Component destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.componentObserver && this.componentObserver.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import { CoreUrlUtilsProvider } from '@providers/utils/url';
|
|||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreSitePluginsProvider } from './siteplugins';
|
||||
import { CoreCompileProvider } from '@core/compile/providers/compile';
|
||||
import { CoreQuestionProvider } from '@core/question/providers/question';
|
||||
|
||||
// Delegates
|
||||
import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate';
|
||||
|
@ -36,6 +37,7 @@ import { CoreUserDelegate } from '@core/user/providers/user-delegate';
|
|||
import { CoreUserProfileFieldDelegate } from '@core/user/providers/user-profile-field-delegate';
|
||||
import { CoreSettingsDelegate } from '@core/settings/providers/delegate';
|
||||
import { CoreQuestionDelegate } from '@core/question/providers/delegate';
|
||||
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
|
||||
import { AddonMessageOutputDelegate } from '@addon/messageoutput/providers/delegate';
|
||||
|
||||
// Handler classes.
|
||||
|
@ -48,6 +50,7 @@ import { CoreSitePluginsUserProfileHandler } from '../classes/user-handler';
|
|||
import { CoreSitePluginsUserProfileFieldHandler } from '../classes/user-profile-field-handler';
|
||||
import { CoreSitePluginsSettingsHandler } from '../classes/settings-handler';
|
||||
import { CoreSitePluginsQuestionHandler } from '../classes/question-handler';
|
||||
import { CoreSitePluginsQuestionBehaviourHandler } from '../classes/question-behaviour-handler';
|
||||
import { CoreSitePluginsMessageOutputHandler } from '../classes/message-output-handler';
|
||||
|
||||
/**
|
||||
|
@ -72,6 +75,7 @@ export class CoreSitePluginsHelperProvider {
|
|||
private courseFormatDelegate: CoreCourseFormatDelegate, private profileFieldDelegate: CoreUserProfileFieldDelegate,
|
||||
private textUtils: CoreTextUtilsProvider, private filepoolProvider: CoreFilepoolProvider,
|
||||
private settingsDelegate: CoreSettingsDelegate, private questionDelegate: CoreQuestionDelegate,
|
||||
private questionBehaviourDelegate: CoreQuestionBehaviourDelegate, private questionProvider: CoreQuestionProvider,
|
||||
private messageOutputDelegate: AddonMessageOutputDelegate) {
|
||||
|
||||
this.logger = logger.getInstance('CoreSitePluginsHelperProvider');
|
||||
|
@ -450,6 +454,10 @@ export class CoreSitePluginsHelperProvider {
|
|||
promise = Promise.resolve(this.registerQuestionHandler(plugin, handlerName, handlerSchema, result));
|
||||
break;
|
||||
|
||||
case 'CoreQuestionBehaviourDelegate':
|
||||
promise = Promise.resolve(this.registerQuestionBehaviourHandler(plugin, handlerName, handlerSchema, result));
|
||||
break;
|
||||
|
||||
case 'AddonMessageOutputDelegate':
|
||||
promise = Promise.resolve(this.registerMessageOutputHandler(plugin, handlerName, handlerSchema, result));
|
||||
break;
|
||||
|
@ -666,6 +674,57 @@ export class CoreSitePluginsHelperProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a handler in an plugin, register it in the question behaviour delegate.
|
||||
*
|
||||
* @param {any} plugin Data of the plugin.
|
||||
* @param {string} handlerName Name of the handler in the plugin.
|
||||
* @param {any} handlerSchema Data about the handler.
|
||||
* @param {any} initResult Result of the init WS call.
|
||||
* @return {string|Promise<string>} A string (or a promise resolved with a string) to identify the handler.
|
||||
*/
|
||||
protected registerQuestionBehaviourHandler(plugin: any, handlerName: string, handlerSchema: any, initResult: any)
|
||||
: string | Promise<string> {
|
||||
if (!handlerSchema.method) {
|
||||
// Required data not provided, stop.
|
||||
this.logger.warn('Ignore site plugin because it doesn\'t provide method', plugin, handlerSchema);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.debug('Register site plugin in question behaviour delegate:', plugin, handlerSchema, initResult);
|
||||
|
||||
// Execute the main method and its JS. The template returned will be used in the question component.
|
||||
return this.executeMethodAndJS(plugin, handlerSchema.method).then((result) => {
|
||||
|
||||
// Create and register the handler.
|
||||
const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName),
|
||||
type = plugin.component.replace('qbehaviour_', ''),
|
||||
behaviourHandler = new CoreSitePluginsQuestionBehaviourHandler(this.questionProvider, uniqueName, type,
|
||||
result.templates.length);
|
||||
|
||||
// Store in handlerSchema some data required by the component.
|
||||
handlerSchema.methodTemplates = result.templates;
|
||||
handlerSchema.methodJSResult = result.jsResult;
|
||||
|
||||
if (result && result.jsResult) {
|
||||
// Override default handler functions with the result of the method JS.
|
||||
for (const property in behaviourHandler) {
|
||||
if (property != 'constructor' && typeof behaviourHandler[property] == 'function' &&
|
||||
typeof result.jsResult[property] == 'function') {
|
||||
behaviourHandler[property] = result.jsResult[property].bind(behaviourHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.questionBehaviourDelegate.registerHandler(behaviourHandler);
|
||||
|
||||
return plugin.component;
|
||||
}).catch((err) => {
|
||||
this.logger.error('Error executing main method for question', handlerSchema.method, err);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a handler in an plugin, register it in the settings delegate.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue