MOBILE-3651 quiz: Implement access rules
parent
e565df9ea4
commit
14ba4869a3
|
@ -0,0 +1,43 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { AddonModQuizAccessDelayBetweenAttemptsModule } from './delaybetweenattempts/delaybetweenattempts.module';
|
||||||
|
import { AddonModQuizAccessIpAddressModule } from './ipaddress/ipaddress.module';
|
||||||
|
import { AddonModQuizAccessNumAttemptsModule } from './numattempts/numattempts.module';
|
||||||
|
import { AddonModQuizAccessOfflineAttemptsModule } from './offlineattempts/offlineattempts.module';
|
||||||
|
import { AddonModQuizAccessOpenCloseDateModule } from './openclosedate/openclosedate.module';
|
||||||
|
import { AddonModQuizAccessPasswordModule } from './password/password.module';
|
||||||
|
import { AddonModQuizAccessSafeBrowserModule } from './safebrowser/safebrowser.module';
|
||||||
|
import { AddonModQuizAccessSecureWindowModule } from './securewindow/securewindow.module';
|
||||||
|
import { AddonModQuizAccessTimeLimitModule } from './timelimit/timelimit.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [],
|
||||||
|
imports: [
|
||||||
|
AddonModQuizAccessDelayBetweenAttemptsModule,
|
||||||
|
AddonModQuizAccessIpAddressModule,
|
||||||
|
AddonModQuizAccessNumAttemptsModule,
|
||||||
|
AddonModQuizAccessOfflineAttemptsModule,
|
||||||
|
AddonModQuizAccessOpenCloseDateModule,
|
||||||
|
AddonModQuizAccessPasswordModule,
|
||||||
|
AddonModQuizAccessSafeBrowserModule,
|
||||||
|
AddonModQuizAccessSecureWindowModule,
|
||||||
|
AddonModQuizAccessTimeLimitModule,
|
||||||
|
],
|
||||||
|
providers: [],
|
||||||
|
exports: [],
|
||||||
|
})
|
||||||
|
export class AddonModQuizAccessRulesModule { }
|
|
@ -0,0 +1,34 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { AddonModQuizAccessRuleDelegate } from '../../services/access-rules-delegate';
|
||||||
|
import { AddonModQuizAccessDelayBetweenAttemptsHandler } from './services/handlers/delaybetweenattempts';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
deps: [],
|
||||||
|
useFactory: () => () => {
|
||||||
|
AddonModQuizAccessRuleDelegate.instance.registerHandler(AddonModQuizAccessDelayBetweenAttemptsHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModQuizAccessDelayBetweenAttemptsModule {}
|
|
@ -0,0 +1,54 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { AddonModQuizAccessRuleHandler } from '@addons/mod/quiz/services/access-rules-delegate';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support delay between attempts access rule.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModQuizAccessDelayBetweenAttemptsHandlerService implements AddonModQuizAccessRuleHandler {
|
||||||
|
|
||||||
|
name = 'AddonModQuizAccessDelayBetweenAttempts';
|
||||||
|
ruleName = 'quizaccess_delaybetweenattempts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return True or promise resolved with true if enabled.
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the rule requires a preflight check when prefetch/start/continue an attempt.
|
||||||
|
*
|
||||||
|
* @param quiz The quiz the rule belongs to.
|
||||||
|
* @param attempt The attempt started/continued. If not supplied, user is starting a new attempt.
|
||||||
|
* @param prefetch Whether the user is prefetching the quiz.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Whether the rule requires a preflight check.
|
||||||
|
*/
|
||||||
|
isPreflightCheckRequired(): boolean | Promise<boolean> {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddonModQuizAccessDelayBetweenAttemptsHandler
|
||||||
|
extends makeSingleton(AddonModQuizAccessDelayBetweenAttemptsHandlerService) {}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { AddonModQuizAccessRuleDelegate } from '../../services/access-rules-delegate';
|
||||||
|
import { AddonModQuizAccessIpAddressHandler } from './services/handlers/ipaddress';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
deps: [],
|
||||||
|
useFactory: () => () => {
|
||||||
|
AddonModQuizAccessRuleDelegate.instance.registerHandler(AddonModQuizAccessIpAddressHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModQuizAccessIpAddressModule {}
|
|
@ -0,0 +1,53 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { AddonModQuizAccessRuleHandler } from '@addons/mod/quiz/services/access-rules-delegate';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support IP address access rule.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModQuizAccessIpAddressHandlerService implements AddonModQuizAccessRuleHandler {
|
||||||
|
|
||||||
|
name = 'AddonModQuizAccessIpAddress';
|
||||||
|
ruleName = 'quizaccess_ipaddress';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return True or promise resolved with true if enabled.
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the rule requires a preflight check when prefetch/start/continue an attempt.
|
||||||
|
*
|
||||||
|
* @param quiz The quiz the rule belongs to.
|
||||||
|
* @param attempt The attempt started/continued. If not supplied, user is starting a new attempt.
|
||||||
|
* @param prefetch Whether the user is prefetching the quiz.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Whether the rule requires a preflight check.
|
||||||
|
*/
|
||||||
|
isPreflightCheckRequired(): boolean | Promise<boolean> {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddonModQuizAccessIpAddressHandler extends makeSingleton(AddonModQuizAccessIpAddressHandlerService) {}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { AddonModQuizAccessRuleDelegate } from '../../services/access-rules-delegate';
|
||||||
|
import { AddonModQuizAccessNumAttemptsHandler } from './services/handlers/numattempts';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
deps: [],
|
||||||
|
useFactory: () => () => {
|
||||||
|
AddonModQuizAccessRuleDelegate.instance.registerHandler(AddonModQuizAccessNumAttemptsHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModQuizAccessNumAttemptsModule {}
|
|
@ -0,0 +1,53 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { AddonModQuizAccessRuleHandler } from '@addons/mod/quiz/services/access-rules-delegate';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support num attempts access rule.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModQuizAccessNumAttemptsHandlerService implements AddonModQuizAccessRuleHandler {
|
||||||
|
|
||||||
|
name = 'AddonModQuizAccessNumAttempts';
|
||||||
|
ruleName = 'quizaccess_numattempts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return True or promise resolved with true if enabled.
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the rule requires a preflight check when prefetch/start/continue an attempt.
|
||||||
|
*
|
||||||
|
* @param quiz The quiz the rule belongs to.
|
||||||
|
* @param attempt The attempt started/continued. If not supplied, user is starting a new attempt.
|
||||||
|
* @param prefetch Whether the user is prefetching the quiz.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Whether the rule requires a preflight check.
|
||||||
|
*/
|
||||||
|
isPreflightCheckRequired(): boolean | Promise<boolean> {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddonModQuizAccessNumAttemptsHandler extends makeSingleton(AddonModQuizAccessNumAttemptsHandlerService) {}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<h3 class="item-heading">{{ 'core.settings.synchronization' | translate }}</h3>
|
||||||
|
<p>{{ 'addon.mod_quiz.confirmcontinueoffline' | translate:{$a: syncTimeReadable} }}</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
|
@ -0,0 +1,56 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '@addons/mod/quiz/services/quiz';
|
||||||
|
import { AddonModQuizSync } from '@addons/mod/quiz/services/quiz-sync';
|
||||||
|
import { Component, OnInit, Input } from '@angular/core';
|
||||||
|
import { FormGroup, FormBuilder } from '@angular/forms';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to render the preflight for offline attempts.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'addon-mod-quiz-access-offline-attempts',
|
||||||
|
templateUrl: 'addon-mod-quiz-access-offline-attempts.html',
|
||||||
|
})
|
||||||
|
export class AddonModQuizAccessOfflineAttemptsComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() rule?: string; // The name of the rule.
|
||||||
|
@Input() quiz?: AddonModQuizQuizWSData; // The quiz the rule belongs to.
|
||||||
|
@Input() attempt?: AddonModQuizAttemptWSData; // The attempt being started/continued.
|
||||||
|
@Input() prefetch?: boolean; // Whether the user is prefetching the quiz.
|
||||||
|
@Input() siteId?: string; // Site ID.
|
||||||
|
@Input() form?: FormGroup; // Form where to add the form control.
|
||||||
|
|
||||||
|
syncTimeReadable = '';
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component being initialized.
|
||||||
|
*/
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
// Always set confirmdatasaved to 1. Sending the data means the user accepted.
|
||||||
|
this.form?.addControl('confirmdatasaved', this.fb.control(1));
|
||||||
|
|
||||||
|
if (!this.quiz) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const time = await AddonModQuizSync.instance.getSyncTime(this.quiz.id);
|
||||||
|
|
||||||
|
this.syncTimeReadable = AddonModQuizSync.instance.getReadableTimeFromTimestamp(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
|
import { AddonModQuizAccessOfflineAttemptsComponent } from './component/offlineattempts';
|
||||||
|
import { AddonModQuizAccessRuleDelegate } from '../../services/access-rules-delegate';
|
||||||
|
import { AddonModQuizAccessOfflineAttemptsHandler } from './services/handlers/offlineattempts';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonModQuizAccessOfflineAttemptsComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreSharedModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
deps: [],
|
||||||
|
useFactory: () => () => {
|
||||||
|
AddonModQuizAccessRuleDelegate.instance.registerHandler(AddonModQuizAccessOfflineAttemptsHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
AddonModQuizAccessOfflineAttemptsComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModQuizAccessOfflineAttemptsModule {}
|
|
@ -0,0 +1,102 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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, Type } from '@angular/core';
|
||||||
|
|
||||||
|
import { AddonModQuizAccessRuleHandler } from '@addons/mod/quiz/services/access-rules-delegate';
|
||||||
|
import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '@addons/mod/quiz/services/quiz';
|
||||||
|
import { AddonModQuizAccessOfflineAttemptsComponent } from '../../component/offlineattempts';
|
||||||
|
import { AddonModQuizSync } from '@addons/mod/quiz/services/quiz-sync';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support offline attempts access rule.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModQuizAccessOfflineAttemptsHandlerService implements AddonModQuizAccessRuleHandler {
|
||||||
|
|
||||||
|
name = 'AddonModQuizAccessOfflineAttempts';
|
||||||
|
ruleName = 'quizaccess_offlineattempts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add preflight data that doesn't require user interaction. The data should be added to the preflightData param.
|
||||||
|
*
|
||||||
|
* @param quiz The quiz the rule belongs to.
|
||||||
|
* @param preflightData Object where to add the preflight data.
|
||||||
|
* @param attempt The attempt started/continued. If not supplied, user is starting a new attempt.
|
||||||
|
* @param prefetch Whether the user is prefetching the quiz.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when done if async, void if it's synchronous.
|
||||||
|
*/
|
||||||
|
getFixedPreflightData(
|
||||||
|
quiz: AddonModQuizQuizWSData,
|
||||||
|
preflightData: Record<string, string>,
|
||||||
|
): void | Promise<void> {
|
||||||
|
preflightData.confirmdatasaved = '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the Component to use to display the access rule preflight.
|
||||||
|
* Implement this if your access rule requires a preflight check with user interaction.
|
||||||
|
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
||||||
|
*
|
||||||
|
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getPreflightComponent(): Type<unknown> | Promise<Type<unknown>> {
|
||||||
|
return AddonModQuizAccessOfflineAttemptsComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return True or promise resolved with true if enabled.
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the rule requires a preflight check when prefetch/start/continue an attempt.
|
||||||
|
*
|
||||||
|
* @param quiz The quiz the rule belongs to.
|
||||||
|
* @param attempt The attempt started/continued. If not supplied, user is starting a new attempt.
|
||||||
|
* @param prefetch Whether the user is prefetching the quiz.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Whether the rule requires a preflight check.
|
||||||
|
*/
|
||||||
|
async isPreflightCheckRequired(
|
||||||
|
quiz: AddonModQuizQuizWSData,
|
||||||
|
attempt?: AddonModQuizAttemptWSData,
|
||||||
|
prefetch?: boolean,
|
||||||
|
siteId?: string, // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
|
): Promise<boolean> {
|
||||||
|
if (prefetch) {
|
||||||
|
// Don't show the warning if the user is prefetching.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!attempt) {
|
||||||
|
// User is starting a new attempt, show the warning.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const syncTime = await AddonModQuizSync.instance.getSyncTime(quiz.id);
|
||||||
|
|
||||||
|
// Show warning if last sync was a while ago.
|
||||||
|
return Date.now() - AddonModQuizSync.instance.syncInterval > syncTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddonModQuizAccessOfflineAttemptsHandler extends makeSingleton(AddonModQuizAccessOfflineAttemptsHandlerService) {}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { AddonModQuizAccessRuleDelegate } from '../../services/access-rules-delegate';
|
||||||
|
import { AddonModQuizAccessOpenCloseDateHandler } from './services/handlers/openclosedate';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
deps: [],
|
||||||
|
useFactory: () => () => {
|
||||||
|
AddonModQuizAccessRuleDelegate.instance.registerHandler(AddonModQuizAccessOpenCloseDateHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModQuizAccessOpenCloseDateModule {}
|
|
@ -0,0 +1,76 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { AddonModQuizAccessRuleHandler } from '@addons/mod/quiz/services/access-rules-delegate';
|
||||||
|
import { AddonModQuizAttemptWSData, AddonModQuizProvider } from '@addons/mod/quiz/services/quiz';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support open/close date access rule.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModQuizAccessOpenCloseDateHandlerService implements AddonModQuizAccessRuleHandler {
|
||||||
|
|
||||||
|
name = 'AddonModQuizAccessOpenCloseDate';
|
||||||
|
ruleName = 'quizaccess_openclosedate';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return True or promise resolved with true if enabled.
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the rule requires a preflight check when prefetch/start/continue an attempt.
|
||||||
|
*
|
||||||
|
* @param quiz The quiz the rule belongs to.
|
||||||
|
* @param attempt The attempt started/continued. If not supplied, user is starting a new attempt.
|
||||||
|
* @param prefetch Whether the user is prefetching the quiz.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Whether the rule requires a preflight check.
|
||||||
|
*/
|
||||||
|
isPreflightCheckRequired(): boolean | Promise<boolean> {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the time left of an attempt should be displayed.
|
||||||
|
*
|
||||||
|
* @param attempt The attempt.
|
||||||
|
* @param endTime The attempt end time (in seconds).
|
||||||
|
* @param timeNow The current time in seconds.
|
||||||
|
* @return Whether it should be displayed.
|
||||||
|
*/
|
||||||
|
shouldShowTimeLeft(attempt: AddonModQuizAttemptWSData, endTime: number, timeNow: number): boolean {
|
||||||
|
// If this is a teacher preview after the close date, do not show the time.
|
||||||
|
if (attempt.preview && timeNow > endTime) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the time left only if it's less than QUIZ_SHOW_TIME_BEFORE_DEADLINE.
|
||||||
|
if (timeNow > endTime - AddonModQuizProvider.QUIZ_SHOW_TIME_BEFORE_DEADLINE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddonModQuizAccessOpenCloseDateHandler extends makeSingleton(AddonModQuizAccessOpenCloseDateHandlerService) {}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<h3 class="item-heading">{{ 'addon.mod_quiz.quizpassword' | translate }}</h3>
|
||||||
|
<p>{{ 'addon.mod_quiz.requirepasswordmessage' | translate}}</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item [formGroup]="form">
|
||||||
|
<ion-label></ion-label>
|
||||||
|
<core-show-password [name]="'quizpassword'">
|
||||||
|
<ion-input id="addon-mod_quiz-accessrule-password-input" name="quizpassword" type="password"
|
||||||
|
placeholder="{{ 'addon.mod_quiz.quizpassword' | translate }}" [formControlName]="'quizpassword'" [clearOnEdit]="false">
|
||||||
|
</ion-input>
|
||||||
|
</core-show-password>
|
||||||
|
</ion-item>
|
|
@ -0,0 +1,46 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 } from '@angular/core';
|
||||||
|
import { FormGroup, FormBuilder } from '@angular/forms';
|
||||||
|
|
||||||
|
import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '@addons/mod/quiz/services/quiz';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to render the preflight for password.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'addon-mod-quiz-access-password',
|
||||||
|
templateUrl: 'addon-mod-quiz-access-password.html',
|
||||||
|
})
|
||||||
|
export class AddonModQuizAccessPasswordComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() rule?: string; // The name of the rule.
|
||||||
|
@Input() quiz?: AddonModQuizQuizWSData; // The quiz the rule belongs to.
|
||||||
|
@Input() attempt?: AddonModQuizAttemptWSData; // The attempt being started/continued.
|
||||||
|
@Input() prefetch?: boolean; // Whether the user is prefetching the quiz.
|
||||||
|
@Input() siteId?: string; // Site ID.
|
||||||
|
@Input() form?: FormGroup; // Form where to add the form control.
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component being initialized.
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Add the control for the password.
|
||||||
|
this.form?.addControl('quizpassword', this.fb.control(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { CoreSharedModule } from '@/core/shared.module';
|
||||||
|
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||||
|
import { AddonModQuizAccessPasswordComponent } from './component/password';
|
||||||
|
import { AddonModQuizAccessPasswordHandler } from './services/handlers/password';
|
||||||
|
import { AddonModQuizAccessRuleDelegate } from '../../services/access-rules-delegate';
|
||||||
|
import { SITE_SCHEMA } from './services/database/password';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonModQuizAccessPasswordComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreSharedModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: CORE_SITE_SCHEMAS,
|
||||||
|
useValue: [SITE_SCHEMA],
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
deps: [],
|
||||||
|
useFactory: () => () => {
|
||||||
|
AddonModQuizAccessRuleDelegate.instance.registerHandler(AddonModQuizAccessPasswordHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
AddonModQuizAccessPasswordComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModQuizAccessPasswordModule {}
|
|
@ -0,0 +1,53 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { CoreSiteSchema } from '@services/sites';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database variables for AddonModQuizAccessPasswordHandlerService.
|
||||||
|
*/
|
||||||
|
export const PASSWORD_TABLE_NAME = 'addon_mod_quiz_access_password';
|
||||||
|
export const SITE_SCHEMA: CoreSiteSchema = {
|
||||||
|
name: 'AddonModQuizAccessPasswordHandler',
|
||||||
|
version: 1,
|
||||||
|
tables: [
|
||||||
|
{
|
||||||
|
name: PASSWORD_TABLE_NAME,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'id',
|
||||||
|
type: 'INTEGER',
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'password',
|
||||||
|
type: 'TEXT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'timemodified',
|
||||||
|
type: 'INTEGER',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quiz attempt.
|
||||||
|
*/
|
||||||
|
export type AddonModQuizAccessPasswordDBRecord = {
|
||||||
|
id: number;
|
||||||
|
password: string;
|
||||||
|
timemodified: number;
|
||||||
|
};
|
|
@ -0,0 +1,198 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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, Type } from '@angular/core';
|
||||||
|
|
||||||
|
import { AddonModQuizAccessRuleHandler } from '@addons/mod/quiz/services/access-rules-delegate';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '@addons/mod/quiz/services/quiz';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { AddonModQuizAccessPasswordDBRecord, PASSWORD_TABLE_NAME } from '../database/password';
|
||||||
|
import { AddonModQuizAccessPasswordComponent } from '../../component/password';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support password access rule.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModQuizAccessPasswordHandlerService implements AddonModQuizAccessRuleHandler {
|
||||||
|
|
||||||
|
name = 'AddonModQuizAccessPassword';
|
||||||
|
ruleName = 'quizaccess_password';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add preflight data that doesn't require user interaction. The data should be added to the preflightData param.
|
||||||
|
*
|
||||||
|
* @param quiz The quiz the rule belongs to.
|
||||||
|
* @param preflightData Object where to add the preflight data.
|
||||||
|
* @param attempt The attempt started/continued. If not supplied, user is starting a new attempt.
|
||||||
|
* @param prefetch Whether the user is prefetching the quiz.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when done if async, void if it's synchronous.
|
||||||
|
*/
|
||||||
|
async getFixedPreflightData(
|
||||||
|
quiz: AddonModQuizQuizWSData,
|
||||||
|
preflightData: Record<string, string>,
|
||||||
|
attempt?: AddonModQuizAttemptWSData,
|
||||||
|
prefetch?: boolean,
|
||||||
|
siteId?: string,
|
||||||
|
): Promise<void> {
|
||||||
|
if (typeof preflightData.quizpassword != 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Try to get a password stored. If it's found, use it.
|
||||||
|
const entry = await this.getPasswordEntry(quiz.id, siteId);
|
||||||
|
|
||||||
|
preflightData.quizpassword = entry.password;
|
||||||
|
} catch {
|
||||||
|
// No password stored.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a password stored in DB.
|
||||||
|
*
|
||||||
|
* @param quizId Quiz ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved with the DB entry on success.
|
||||||
|
*/
|
||||||
|
protected async getPasswordEntry(quizId: number, siteId?: string): Promise<AddonModQuizAccessPasswordDBRecord> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
return site.getDb().getRecord(PASSWORD_TABLE_NAME, { id: quizId });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the Component to use to display the access rule preflight.
|
||||||
|
* Implement this if your access rule requires a preflight check with user interaction.
|
||||||
|
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
||||||
|
*
|
||||||
|
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getPreflightComponent(): Type<unknown> | Promise<Type<unknown>> {
|
||||||
|
return AddonModQuizAccessPasswordComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return True or promise resolved with true if enabled.
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the rule requires a preflight check when prefetch/start/continue an attempt.
|
||||||
|
*
|
||||||
|
* @param quiz The quiz the rule belongs to.
|
||||||
|
* @param attempt The attempt started/continued. If not supplied, user is starting a new attempt.
|
||||||
|
* @param prefetch Whether the user is prefetching the quiz.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Whether the rule requires a preflight check.
|
||||||
|
*/
|
||||||
|
async isPreflightCheckRequired(
|
||||||
|
quiz: AddonModQuizQuizWSData,
|
||||||
|
attempt?: AddonModQuizAttemptWSData,
|
||||||
|
prefetch?: boolean,
|
||||||
|
siteId?: string,
|
||||||
|
): Promise<boolean> {
|
||||||
|
// If there's a password stored don't require the preflight since we'll use the stored one.
|
||||||
|
const entry = await CoreUtils.instance.ignoreErrors(this.getPasswordEntry(quiz.id, siteId));
|
||||||
|
|
||||||
|
return !entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function called when the preflight check has passed. This is a chance to record that fact in some way.
|
||||||
|
*
|
||||||
|
* @param quiz The quiz the rule belongs to.
|
||||||
|
* @param attempt The attempt started/continued.
|
||||||
|
* @param preflightData Preflight data gathered.
|
||||||
|
* @param prefetch Whether the user is prefetching the quiz.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when done if async, void if it's synchronous.
|
||||||
|
*/
|
||||||
|
async notifyPreflightCheckPassed(
|
||||||
|
quiz: AddonModQuizQuizWSData,
|
||||||
|
attempt: AddonModQuizAttemptWSData | undefined,
|
||||||
|
preflightData: Record<string, string>,
|
||||||
|
prefetch?: boolean,
|
||||||
|
siteId?: string,
|
||||||
|
): Promise<void> {
|
||||||
|
// The password is right, store it to use it automatically in following executions.
|
||||||
|
if (typeof preflightData.quizpassword != 'undefined') {
|
||||||
|
return this.storePassword(quiz.id, preflightData.quizpassword, siteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function called when the preflight check fails. This is a chance to record that fact in some way.
|
||||||
|
*
|
||||||
|
* @param quiz The quiz the rule belongs to.
|
||||||
|
* @param attempt The attempt started/continued.
|
||||||
|
* @param preflightData Preflight data gathered.
|
||||||
|
* @param prefetch Whether the user is prefetching the quiz.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when done if async, void if it's synchronous.
|
||||||
|
*/
|
||||||
|
notifyPreflightCheckFailed?(
|
||||||
|
quiz: AddonModQuizQuizWSData,
|
||||||
|
attempt: AddonModQuizAttemptWSData | undefined,
|
||||||
|
preflightData: Record<string, string>,
|
||||||
|
prefetch?: boolean,
|
||||||
|
siteId?: string,
|
||||||
|
): Promise<void> {
|
||||||
|
// The password is wrong, remove it from DB if it's there.
|
||||||
|
return this.removePassword(quiz.id, siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a password from DB.
|
||||||
|
*
|
||||||
|
* @param quizId Quiz ID.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async removePassword(quizId: number, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
await site.getDb().deleteRecords(PASSWORD_TABLE_NAME, { id: quizId });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a password in DB.
|
||||||
|
*
|
||||||
|
* @param quizId Quiz ID.
|
||||||
|
* @param password Password.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async storePassword(quizId: number, password: string, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
|
||||||
|
const entry: AddonModQuizAccessPasswordDBRecord = {
|
||||||
|
id: quizId,
|
||||||
|
password,
|
||||||
|
timemodified: Date.now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await site.getDb().insertRecord(PASSWORD_TABLE_NAME, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddonModQuizAccessPasswordHandler extends makeSingleton(AddonModQuizAccessPasswordHandlerService) {}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { AddonModQuizAccessRuleDelegate } from '../../services/access-rules-delegate';
|
||||||
|
import { AddonModQuizAccessSafeBrowserHandler } from './services/handlers/safebrowser';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
deps: [],
|
||||||
|
useFactory: () => () => {
|
||||||
|
AddonModQuizAccessRuleDelegate.instance.registerHandler(AddonModQuizAccessSafeBrowserHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModQuizAccessSafeBrowserModule {}
|
|
@ -0,0 +1,53 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { AddonModQuizAccessRuleHandler } from '@addons/mod/quiz/services/access-rules-delegate';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support safe address access rule.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModQuizAccessSafeBrowserHandlerService implements AddonModQuizAccessRuleHandler {
|
||||||
|
|
||||||
|
name = 'AddonModQuizAccessSafeBrowser';
|
||||||
|
ruleName = 'quizaccess_safebrowser';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return True or promise resolved with true if enabled.
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the rule requires a preflight check when prefetch/start/continue an attempt.
|
||||||
|
*
|
||||||
|
* @param quiz The quiz the rule belongs to.
|
||||||
|
* @param attempt The attempt started/continued. If not supplied, user is starting a new attempt.
|
||||||
|
* @param prefetch Whether the user is prefetching the quiz.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Whether the rule requires a preflight check.
|
||||||
|
*/
|
||||||
|
isPreflightCheckRequired(): boolean | Promise<boolean> {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddonModQuizAccessSafeBrowserHandler extends makeSingleton(AddonModQuizAccessSafeBrowserHandlerService) {}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { AddonModQuizAccessRuleDelegate } from '../../services/access-rules-delegate';
|
||||||
|
import { AddonModQuizAccessSecureWindowHandler } from './services/handlers/securewindow';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
deps: [],
|
||||||
|
useFactory: () => () => {
|
||||||
|
AddonModQuizAccessRuleDelegate.instance.registerHandler(AddonModQuizAccessSecureWindowHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModQuizAccessSecureWindowModule {}
|
|
@ -0,0 +1,53 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { AddonModQuizAccessRuleHandler } from '@addons/mod/quiz/services/access-rules-delegate';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support secure window access rule.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModQuizAccessSecureWindowHandlerService implements AddonModQuizAccessRuleHandler {
|
||||||
|
|
||||||
|
name = 'AddonModQuizAccessSecureWindow';
|
||||||
|
ruleName = 'quizaccess_securewindow';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return True or promise resolved with true if enabled.
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the rule requires a preflight check when prefetch/start/continue an attempt.
|
||||||
|
*
|
||||||
|
* @param quiz The quiz the rule belongs to.
|
||||||
|
* @param attempt The attempt started/continued. If not supplied, user is starting a new attempt.
|
||||||
|
* @param prefetch Whether the user is prefetching the quiz.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Whether the rule requires a preflight check.
|
||||||
|
*/
|
||||||
|
isPreflightCheckRequired(): boolean | Promise<boolean> {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddonModQuizAccessSecureWindowHandler extends makeSingleton(AddonModQuizAccessSecureWindowHandlerService) {}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<h3 class="item-heading">{{ 'addon.mod_quiz.confirmstartheader' | translate }}</h3>
|
||||||
|
<p>{{ 'addon.mod_quiz.confirmstart' | translate:{$a: readableTimeLimit} }}</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
|
@ -0,0 +1,47 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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, Input, OnInit } from '@angular/core';
|
||||||
|
import { FormGroup } from '@angular/forms';
|
||||||
|
|
||||||
|
import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '@addons/mod/quiz/services/quiz';
|
||||||
|
import { CoreTimeUtils } from '@services/utils/time';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to render the preflight for time limit.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'addon-mod-quiz-access-time-limit',
|
||||||
|
templateUrl: 'addon-mod-quiz-access-time-limit.html',
|
||||||
|
})
|
||||||
|
export class AddonModQuizAccessTimeLimitComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() rule?: string; // The name of the rule.
|
||||||
|
@Input() quiz?: AddonModQuizQuizWSData; // The quiz the rule belongs to.
|
||||||
|
@Input() attempt?: AddonModQuizAttemptWSData; // The attempt being started/continued.
|
||||||
|
@Input() prefetch?: boolean; // Whether the user is prefetching the quiz.
|
||||||
|
@Input() siteId?: string; // Site ID.
|
||||||
|
@Input() form?: FormGroup; // Form where to add the form control.
|
||||||
|
|
||||||
|
readableTimeLimit = '';
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if (!this.quiz?.timelimit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.readableTimeLimit = CoreTimeUtils.instance.formatTime(this.quiz?.timelimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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, Type } from '@angular/core';
|
||||||
|
|
||||||
|
import { AddonModQuizAccessRuleHandler } from '@addons/mod/quiz/services/access-rules-delegate';
|
||||||
|
import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from '@addons/mod/quiz/services/quiz';
|
||||||
|
import { AddonModQuizAccessTimeLimitComponent } from '../../component/timelimit';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support time limit access rule.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonModQuizAccessTimeLimitHandlerService implements AddonModQuizAccessRuleHandler {
|
||||||
|
|
||||||
|
name = 'AddonModQuizAccessTimeLimit';
|
||||||
|
ruleName = 'quizaccess_timelimit';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the Component to use to display the access rule preflight.
|
||||||
|
* Implement this if your access rule requires a preflight check with user interaction.
|
||||||
|
* It's recommended to return the class of the component, but you can also return an instance of the component.
|
||||||
|
*
|
||||||
|
* @return The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getPreflightComponent(): Type<unknown> | Promise<Type<unknown>> {
|
||||||
|
return AddonModQuizAccessTimeLimitComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return True or promise resolved with true if enabled.
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the rule requires a preflight check when prefetch/start/continue an attempt.
|
||||||
|
*
|
||||||
|
* @param quiz The quiz the rule belongs to.
|
||||||
|
* @param attempt The attempt started/continued. If not supplied, user is starting a new attempt.
|
||||||
|
* @param prefetch Whether the user is prefetching the quiz.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Whether the rule requires a preflight check.
|
||||||
|
*/
|
||||||
|
isPreflightCheckRequired(
|
||||||
|
quiz: AddonModQuizQuizWSData,
|
||||||
|
attempt?: AddonModQuizAttemptWSData,
|
||||||
|
): boolean | Promise<boolean> {
|
||||||
|
// Warning only required if the attempt is not already started.
|
||||||
|
return !attempt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the time left of an attempt should be displayed.
|
||||||
|
*
|
||||||
|
* @param attempt The attempt.
|
||||||
|
* @param endTime The attempt end time (in seconds).
|
||||||
|
* @param timeNow The current time in seconds.
|
||||||
|
* @return Whether it should be displayed.
|
||||||
|
*/
|
||||||
|
shouldShowTimeLeft(attempt: AddonModQuizAttemptWSData, endTime: number, timeNow: number): boolean {
|
||||||
|
// If this is a teacher preview after the time limit expires, don't show the time left.
|
||||||
|
return !(attempt.preview && timeNow > endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddonModQuizAccessTimeLimitHandler extends makeSingleton(AddonModQuizAccessTimeLimitHandlerService) {}
|
|
@ -0,0 +1,43 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
|
import { AddonModQuizAccessTimeLimitComponent } from './component/timelimit';
|
||||||
|
import { AddonModQuizAccessRuleDelegate } from '../../services/access-rules-delegate';
|
||||||
|
import { AddonModQuizAccessTimeLimitHandler } from './services/handlers/timelimit';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonModQuizAccessTimeLimitComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreSharedModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
deps: [],
|
||||||
|
useFactory: () => () => {
|
||||||
|
AddonModQuizAccessRuleDelegate.instance.registerHandler(AddonModQuizAccessTimeLimitHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
AddonModQuizAccessTimeLimitComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModQuizAccessTimeLimitModule {}
|
|
@ -19,6 +19,7 @@ import { CoreCourseModuleDelegate } from '@features/course/services/module-deleg
|
||||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
|
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
|
||||||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||||
|
import { AddonModQuizAccessRulesModule } from './accessrules/accessrules.module';
|
||||||
import { AddonModQuizComponentsModule } from './components/components.module';
|
import { AddonModQuizComponentsModule } from './components/components.module';
|
||||||
import { SITE_SCHEMA } from './services/database/quiz';
|
import { SITE_SCHEMA } from './services/database/quiz';
|
||||||
import { AddonModQuizModuleHandler, AddonModQuizModuleHandlerService } from './services/handlers/module';
|
import { AddonModQuizModuleHandler, AddonModQuizModuleHandlerService } from './services/handlers/module';
|
||||||
|
@ -35,6 +36,7 @@ const routes: Routes = [
|
||||||
imports: [
|
imports: [
|
||||||
CoreMainMenuTabRoutingModule.forChild(routes),
|
CoreMainMenuTabRoutingModule.forChild(routes),
|
||||||
AddonModQuizComponentsModule,
|
AddonModQuizComponentsModule,
|
||||||
|
AddonModQuizAccessRulesModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue