MOBILE-2376 siteplugins: Support question behaviours
This commit is contained in:
		
							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; | ||||
|     } | ||||
|  | ||||
							
								
								
									
										59
									
								
								src/core/siteplugins/classes/compile-init-component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/core/siteplugins/classes/compile-init-component.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										44
									
								
								src/core/siteplugins/classes/question-behaviour-handler.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/core/siteplugins/classes/question-behaviour-handler.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user