MOBILE-2921 quiz: Support quiz push clicks

main
Dani Palou 2019-03-19 11:58:28 +01:00
parent e8ab5edc2a
commit ac96739e39
4 changed files with 162 additions and 54 deletions

View File

@ -13,13 +13,16 @@
// limitations under the License.
import { Injectable } from '@angular/core';
import { ModalController } from 'ionic-angular';
import { ModalController, NavController } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { CoreSitesProvider } from '@providers/sites';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { AddonModQuizProvider } from './quiz';
import { AddonModQuizOfflineProvider } from './quiz-offline';
import { AddonModQuizAccessRuleDelegate } from './access-rules-delegate';
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
/**
* Helper service that provides some features for quiz.
@ -29,7 +32,9 @@ export class AddonModQuizHelperProvider {
constructor(private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private utils: CoreUtilsProvider,
private accessRuleDelegate: AddonModQuizAccessRuleDelegate, private quizProvider: AddonModQuizProvider,
private modalCtrl: ModalController, private quizOfflineProvider: AddonModQuizOfflineProvider) { }
private modalCtrl: ModalController, private quizOfflineProvider: AddonModQuizOfflineProvider,
private courseHelper: CoreCourseHelperProvider, private sitesProvider: CoreSitesProvider,
private linkHelper: CoreContentLinksHelperProvider) { }
/**
* Validate a preflight data or show a modal to input the preflight data if required.
@ -156,6 +161,76 @@ export class AddonModQuizHelperProvider {
return this.domUtils.getContentsOfElement(element, '.grade');
}
/**
* Get a quiz ID by attempt ID.
*
* @param {number} attemptId Attempt ID.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<number>} Promise resolved with the quiz ID.
*/
getQuizIdByAttemptId(attemptId: number, siteId?: string): Promise<number> {
// Use getAttemptReview to retrieve the quiz ID.
return this.quizProvider.getAttemptReview(attemptId, undefined, false, siteId).then((reviewData) => {
if (reviewData.attempt && reviewData.attempt.quiz) {
return reviewData.attempt.quiz;
}
return Promise.reject(null);
});
}
/**
* Handle a review link.
*
* @param {NavController} navCtrl Nav controller, can be undefined/null.
* @param {number} attemptId Attempt ID.
* @param {number} [page] Page to load, -1 to all questions in same page.
* @param {number} [courseId] Course ID.
* @param {number} [quizId] Quiz ID.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when done.
*/
handleReviewLink(navCtrl: NavController, attemptId: number, page?: number, courseId?: number, quizId?: number,
siteId?: string): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
const modal = this.domUtils.showModalLoading();
let promise;
if (quizId) {
promise = Promise.resolve(quizId);
} else {
// Retrieve the quiz ID using the attempt ID.
promise = this.getQuizIdByAttemptId(attemptId);
}
return promise.then((id) => {
quizId = id;
// Get the courseId if we don't have it.
if (courseId) {
return courseId;
} else {
return this.courseHelper.getModuleCourseIdByInstance(quizId, 'quiz', siteId);
}
}).then((courseId) => {
// Go to the review page.
const pageParams = {
quizId: quizId,
attemptId: attemptId,
courseId: courseId,
page: isNaN(page) ? -1 : page
};
return this.linkHelper.goInSite(navCtrl, 'AddonModQuizReviewPage', pageParams, siteId);
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'An error occurred while loading the required data.');
}).finally(() => {
modal.dismiss();
});
}
/**
* Add some calculated data to the attempt.
*

View File

@ -0,0 +1,74 @@
// (C) Copyright 2015 Martin Dougiamas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreUrlUtilsProvider } from '@providers/utils/url';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CorePushNotificationsClickHandler } from '@core/pushnotifications/providers/delegate';
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
import { AddonModQuizProvider } from './quiz';
import { AddonModQuizHelperProvider } from './helper';
/**
* Handler for quiz push notifications clicks.
*/
@Injectable()
export class AddonModQuizPushClickHandler implements CorePushNotificationsClickHandler {
name = 'AddonModQuizPushClickHandler';
priority = 200;
featureName = 'CoreCourseModuleDelegate_AddonModQuiz';
protected SUPPORTED_NAMES = ['submission', 'confirmation', 'attempt_overdue'];
constructor(private utils: CoreUtilsProvider, private quizProvider: AddonModQuizProvider,
private urlUtils: CoreUrlUtilsProvider, private courseHelper: CoreCourseHelperProvider,
private quizHelper: AddonModQuizHelperProvider) {}
/**
* Check if a notification click is handled by this handler.
*
* @param {any} notification The notification to check.
* @return {boolean} Whether the notification click is handled by this handler
*/
handles(notification: any): boolean | Promise<boolean> {
return this.utils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'mod_quiz' &&
this.SUPPORTED_NAMES.indexOf(notification.name) != -1;
}
/**
* Handle the notification click.
*
* @param {any} notification The notification to check.
* @return {Promise<any>} Promise resolved when done.
*/
handleClick(notification: any): Promise<any> {
const contextUrlParams = this.urlUtils.extractUrlParams(notification.contexturl),
courseId = Number(notification.courseid);
if (notification.name == 'submission') {
// A student made a submission, go to view the attempt.
return this.quizHelper.handleReviewLink(undefined, Number(contextUrlParams.attempt), Number(contextUrlParams.page),
courseId, undefined, notification.site);
} else {
// Open the activity.
const moduleId = Number(contextUrlParams.id);
return this.quizProvider.invalidateContent(moduleId, courseId, notification.site).catch(() => {
// Ignore errors.
}).then(() => {
return this.courseHelper.navigateToModule(moduleId, notification.site, courseId);
});
}
}
}

View File

@ -13,12 +13,10 @@
// limitations under the License.
import { Injectable } from '@angular/core';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
import { AddonModQuizProvider } from './quiz';
import { AddonModQuizHelperProvider } from './helper';
/**
* Handler to treat links to quiz review.
@ -29,8 +27,7 @@ export class AddonModQuizReviewLinkHandler extends CoreContentLinksHandlerBase {
featureName = 'CoreCourseModuleDelegate_AddonModQuiz';
pattern = /\/mod\/quiz\/review\.php.*([\&\?]attempt=\d+)/;
constructor(protected domUtils: CoreDomUtilsProvider, protected quizProvider: AddonModQuizProvider,
protected courseHelper: CoreCourseHelperProvider, protected linkHelper: CoreContentLinksHelperProvider) {
constructor(protected quizProvider: AddonModQuizProvider, protected quizHelper: AddonModQuizHelperProvider) {
super();
}
@ -50,56 +47,13 @@ export class AddonModQuizReviewLinkHandler extends CoreContentLinksHandlerBase {
return [{
action: (siteId, navCtrl?): void => {
// Retrieve the quiz ID using the attempt ID.
const modal = this.domUtils.showModalLoading(),
attemptId = parseInt(params.attempt, 10),
const attemptId = parseInt(params.attempt, 10),
page = parseInt(params.page, 10);
let quizId;
this.getQuizIdByAttemptId(attemptId).then((id) => {
quizId = id;
// Get the courseId if we don't have it.
if (courseId) {
return courseId;
} else {
return this.courseHelper.getModuleCourseIdByInstance(quizId, 'quiz', siteId);
}
}).then((courseId) => {
// Go to the review page.
const pageParams = {
quizId: quizId,
attemptId: attemptId,
courseId: courseId,
page: params.showall ? -1 : (isNaN(page) ? -1 : page)
};
this.linkHelper.goInSite(navCtrl, 'AddonModQuizReviewPage', pageParams, siteId);
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'An error occurred while loading the required data.');
}).finally(() => {
modal.dismiss();
});
this.quizHelper.handleReviewLink(navCtrl, attemptId, page, courseId, undefined, siteId);
}
}];
}
/**
* Get a quiz ID by attempt ID.
*
* @param {number} attemptId Attempt ID.
* @return {Promise<number>} Promise resolved with the quiz ID.
*/
protected getQuizIdByAttemptId(attemptId: number): Promise<number> {
// Use getAttemptReview to retrieve the quiz ID.
return this.quizProvider.getAttemptReview(attemptId).then((reviewData) => {
if (reviewData.attempt && reviewData.attempt.quiz) {
return reviewData.attempt.quiz;
}
return Promise.reject(null);
});
}
/**
* Check if the handler is enabled for a certain site (site + user) and a URL.

View File

@ -17,6 +17,7 @@ import { CoreCronDelegate } from '@providers/cron';
import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate';
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate';
import { AddonModQuizAccessRuleDelegate } from './providers/access-rules-delegate';
import { AddonModQuizProvider } from './providers/quiz';
import { AddonModQuizOfflineProvider } from './providers/quiz-offline';
@ -29,6 +30,7 @@ import { AddonModQuizIndexLinkHandler } from './providers/index-link-handler';
import { AddonModQuizGradeLinkHandler } from './providers/grade-link-handler';
import { AddonModQuizReviewLinkHandler } from './providers/review-link-handler';
import { AddonModQuizListLinkHandler } from './providers/list-link-handler';
import { AddonModQuizPushClickHandler } from './providers/push-click-handler';
import { AddonModQuizComponentsModule } from './components/components.module';
import { CoreUpdateManagerProvider } from '@providers/update-manager';
@ -79,7 +81,8 @@ export const ADDON_MOD_QUIZ_PROVIDERS: any[] = [
AddonModQuizIndexLinkHandler,
AddonModQuizGradeLinkHandler,
AddonModQuizReviewLinkHandler,
AddonModQuizListLinkHandler
AddonModQuizListLinkHandler,
AddonModQuizPushClickHandler
]
})
export class AddonModQuizModule {
@ -88,7 +91,8 @@ export class AddonModQuizModule {
cronDelegate: CoreCronDelegate, syncHandler: AddonModQuizSyncCronHandler, linksDelegate: CoreContentLinksDelegate,
indexHandler: AddonModQuizIndexLinkHandler, gradeHandler: AddonModQuizGradeLinkHandler,
reviewHandler: AddonModQuizReviewLinkHandler, updateManager: CoreUpdateManagerProvider,
listLinkHandler: AddonModQuizListLinkHandler) {
listLinkHandler: AddonModQuizListLinkHandler,
pushNotificationsDelegate: CorePushNotificationsDelegate, pushClickHandler: AddonModQuizPushClickHandler) {
moduleDelegate.registerHandler(moduleHandler);
prefetchDelegate.registerHandler(prefetchHandler);
@ -97,6 +101,7 @@ export class AddonModQuizModule {
linksDelegate.registerHandler(gradeHandler);
linksDelegate.registerHandler(reviewHandler);
linksDelegate.registerHandler(listLinkHandler);
pushNotificationsDelegate.registerClickHandler(pushClickHandler);
// Allow migrating the tables from the old app to the new schema.
updateManager.registerSiteTableMigration({