commit
f1c6e5dd28
|
@ -8,7 +8,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!badgeLoaded" (ionRefresh)="refreshBadges($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!badgeLoaded" (ionRefresh)="refreshBadges($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="badgeLoaded">
|
||||
|
|
|
@ -101,7 +101,7 @@ export class AddonBadgesIssuedBadgePage implements OnInit {
|
|||
*
|
||||
* @param refresher Refresher.
|
||||
*/
|
||||
async refreshBadges(refresher?: CustomEvent<IonRefresher>): Promise<void> {
|
||||
async refreshBadges(refresher?: IonRefresher): Promise<void> {
|
||||
await CoreUtils.ignoreErrors(Promise.all([
|
||||
AddonBadges.invalidateUserBadges(this.courseId, this.userId),
|
||||
]));
|
||||
|
@ -110,7 +110,7 @@ export class AddonBadgesIssuedBadgePage implements OnInit {
|
|||
this.fetchIssuedBadge(),
|
||||
]));
|
||||
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||
|
|
|
@ -250,7 +250,7 @@ export class AddonBlogEntriesPage implements OnInit {
|
|||
*
|
||||
* @param refresher Refresher instance.
|
||||
*/
|
||||
refresh(refresher?: CustomEvent<IonRefresher>): void {
|
||||
refresh(refresher?: IonRefresher): void {
|
||||
const promises = this.entries.map((entry) =>
|
||||
CoreComments.invalidateCommentsData('user', entry.userid, this.component, entry.id, 'format_blog'));
|
||||
|
||||
|
@ -269,7 +269,7 @@ export class AddonBlogEntriesPage implements OnInit {
|
|||
CoreUtils.allPromises(promises).finally(() => {
|
||||
this.fetchEntries(true).finally(() => {
|
||||
if (refresher) {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -431,13 +431,13 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
|
|||
* @param done Function to call when done.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async doRefresh(refresher?: CustomEvent<IonRefresher>, done?: () => void): Promise<void> {
|
||||
async doRefresh(refresher?: IonRefresher, done?: () => void): Promise<void> {
|
||||
if (!this.loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.refreshData(true).finally(() => {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
done && done();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -363,7 +363,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
|||
*
|
||||
* @param refresher Refresher.
|
||||
*/
|
||||
refreshData(refresher?: CustomEvent<IonRefresher>): void {
|
||||
refreshData(refresher?: IonRefresher): void {
|
||||
const promises = [
|
||||
AddonCalendar.invalidateAccessInformation(this.courseId),
|
||||
AddonCalendar.invalidateAllowedEventTypes(this.courseId),
|
||||
|
@ -384,7 +384,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
|
|||
|
||||
Promise.all(promises).finally(() => {
|
||||
this.fetchData().finally(() => {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!eventLoaded" (ionRefresh)="doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!eventLoaded" (ionRefresh)="doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="eventLoaded">
|
||||
|
|
|
@ -402,13 +402,13 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
|||
* @param showErrors Whether to show sync errors to the user.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async doRefresh(refresher?: CustomEvent<IonRefresher>, done?: () => void, showErrors= false): Promise<void> {
|
||||
async doRefresh(refresher?: IonRefresher, done?: () => void, showErrors= false): Promise<void> {
|
||||
if (!this.eventLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.refreshEvent(true, showErrors).finally(() => {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
done && done();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -265,13 +265,13 @@ export class AddonCalendarIndexPage implements OnInit, OnDestroy {
|
|||
* @param showErrors Whether to show sync errors to the user.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async doRefresh(refresher?: CustomEvent<IonRefresher>, done?: () => void, showErrors?: boolean): Promise<void> {
|
||||
async doRefresh(refresher?: IonRefresher, done?: () => void, showErrors?: boolean): Promise<void> {
|
||||
if (!this.loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.refreshData(true, showErrors).finally(() => {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
done && done();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</ion-header>
|
||||
<ion-content>
|
||||
<core-split-view>
|
||||
<ion-refresher slot="fixed" [disabled]="!eventsLoaded" (ionRefresh)="doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!eventsLoaded" (ionRefresh)="doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="eventsLoaded">
|
||||
|
|
|
@ -549,13 +549,13 @@ export class AddonCalendarListPage implements OnInit, OnDestroy {
|
|||
* @param showErrors Whether to show sync errors to the user.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async doRefresh(refresher?: CustomEvent<IonRefresher>, done?: () => void, showErrors?: boolean): Promise<void> {
|
||||
async doRefresh(refresher?: IonRefresher, done?: () => void, showErrors?: boolean): Promise<void> {
|
||||
if (!this.eventsLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.refreshEvents(true, showErrors).finally(() => {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
done && done();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!devicesLoaded" (ionRefresh)="refreshDevices($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!devicesLoaded" (ionRefresh)="refreshDevices($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="devicesLoaded">
|
||||
|
|
|
@ -106,13 +106,13 @@ export class AddonMessageOutputAirnotifierDevicesPage implements OnInit, OnDestr
|
|||
*
|
||||
* @param refresher Refresher.
|
||||
*/
|
||||
async refreshDevices(refresher: CustomEvent<IonRefresher>): Promise<void> {
|
||||
async refreshDevices(refresher: IonRefresher): Promise<void> {
|
||||
try {
|
||||
await CoreUtils.ignoreErrors(AddonMessageOutputAirnotifier.invalidateUserDevices());
|
||||
|
||||
await this.fetchDevices();
|
||||
} finally {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ export class AddonMessagesConversationInfoComponent implements OnInit {
|
|||
* @param refresher Refresher.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async refreshData(refresher?: CustomEvent<IonRefresher>): Promise<void> {
|
||||
async refreshData(refresher?: IonRefresher): Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
promises.push(AddonMessages.invalidateConversation(this.conversationId));
|
||||
|
@ -131,7 +131,7 @@ export class AddonMessagesConversationInfoComponent implements OnInit {
|
|||
await Promise.all(promises);
|
||||
|
||||
await this.fetchData().finally(() => {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</ion-header>
|
||||
<ion-content>
|
||||
<core-split-view>
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ export class AddonMessagesContacts35Page implements OnInit, OnDestroy {
|
|||
* @param refresher Refresher.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async refreshData(refresher?: CustomEvent<IonRefresher>): Promise<void> {
|
||||
async refreshData(refresher?: IonRefresher): Promise<void> {
|
||||
try {
|
||||
if (this.searchString) {
|
||||
// User has searched, update the search.
|
||||
|
@ -139,7 +139,7 @@ export class AddonMessagesContacts35Page implements OnInit, OnDestroy {
|
|||
await this.fetchData();
|
||||
}
|
||||
} finally {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<!-- Contacts tab. -->
|
||||
<core-tab [title]="'addon.messages.contacts' | translate" (ionSelect)="selectTab('confirmed')">
|
||||
<ng-template>
|
||||
<ion-refresher slot="fixed" [disabled]="!confirmedLoaded" (ionRefresh)="refreshData($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!confirmedLoaded" (ionRefresh)="refreshData($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="confirmedLoaded" class="core-loading-center">
|
||||
|
@ -57,7 +57,7 @@
|
|||
<!-- Requests tab. -->
|
||||
<core-tab [title]="'addon.messages.requests' | translate" (ionSelect)="selectTab('requests')" [badge]="requestsBadge">
|
||||
<ng-template>
|
||||
<ion-refresher slot="fixed" [disabled]="!requestsLoaded" (ionRefresh)="refreshData($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!requestsLoaded" (ionRefresh)="refreshData($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="requestsLoaded" class="core-loading-center">
|
||||
|
|
|
@ -185,7 +185,7 @@ export class AddonMessagesContactsPage implements OnInit, OnDestroy {
|
|||
* @param refresher Refresher.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async refreshData(refresher?: CustomEvent<IonRefresher>): Promise<void> {
|
||||
async refreshData(refresher?: IonRefresher): Promise<void> {
|
||||
try {
|
||||
if (this.selected == 'confirmed') {
|
||||
// No need to invalidate contacts, we always try to get the latest.
|
||||
|
@ -198,7 +198,7 @@ export class AddonMessagesContactsPage implements OnInit, OnDestroy {
|
|||
await this.requestsFetchData(true);
|
||||
}
|
||||
} finally {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</ion-header>
|
||||
<ion-content>
|
||||
<core-split-view>
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy {
|
|||
* @param refreshUnreadCounts Whteher to refresh unread counts.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async refreshData(refresher?: CustomEvent<IonRefresher>, refreshUnreadCounts: boolean = true): Promise<void> {
|
||||
async refreshData(refresher?: IonRefresher, refreshUnreadCounts: boolean = true): Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
promises.push(AddonMessages.invalidateDiscussionsCache(this.siteId));
|
||||
|
||||
|
@ -178,7 +178,7 @@ export class AddonMessagesDiscussions35Page implements OnInit, OnDestroy {
|
|||
|
||||
await CoreUtils.allPromises(promises).finally(() => this.fetchData().finally(() => {
|
||||
if (refresher) {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</ion-header>
|
||||
<ion-content class="core-expand-max">
|
||||
<core-split-view>
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded || !currentListEl" (ionRefresh)="refreshData($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded || !currentListEl" (ionRefresh)="refreshData($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -700,7 +700,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
* @param refreshUnreadCounts Whether to refresh unread counts.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async refreshData(refresher?: CustomEvent<IonRefresher>, refreshUnreadCounts: boolean = true): Promise<void> {
|
||||
async refreshData(refresher?: IonRefresher, refreshUnreadCounts: boolean = true): Promise<void> {
|
||||
// Don't invalidate conversations and so, they always try to get latest data.
|
||||
try {
|
||||
await AddonMessages.invalidateContactRequestsCountCache(this.siteId);
|
||||
|
@ -709,7 +709,7 @@ export class AddonMessagesGroupConversationsPage implements OnInit, OnDestroy {
|
|||
await this.fetchData(refreshUnreadCounts);
|
||||
} finally {
|
||||
if (refresher) {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!preferencesLoaded" (ionRefresh)="refreshPreferences($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!preferencesLoaded" (ionRefresh)="refreshPreferences($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="preferencesLoaded">
|
||||
|
|
|
@ -251,10 +251,10 @@ export class AddonMessagesSettingsPage implements OnInit, OnDestroy {
|
|||
*
|
||||
* @param refresher Refresher.
|
||||
*/
|
||||
refreshPreferences(refresher?: CustomEvent<IonRefresher>): void {
|
||||
refreshPreferences(refresher?: IonRefresher): void {
|
||||
AddonMessages.invalidateMessagePreferences().finally(() => {
|
||||
this.fetchPreferences().finally(() => {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<core-format-text [text]="description" [component]="component" [componentId]="componentId" maxHeight="120"
|
||||
contextLevel="module" [contextInstanceId]="module!.id" [courseId]="courseId" (click)="expandDescription($event)">
|
||||
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId" (click)="expandDescription($event)">
|
||||
</core-format-text>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
@ -136,7 +136,7 @@
|
|||
|
||||
<!-- If it's a student, display his submission. -->
|
||||
<addon-mod-assign-submission *ngIf="loaded && !canViewAllSubmissions && canViewOwnSubmission" [courseId]="courseId"
|
||||
[moduleId]="module!.id">
|
||||
[moduleId]="module.id">
|
||||
</addon-mod-assign-submission>
|
||||
|
||||
</core-loading>
|
||||
|
|
|
@ -120,7 +120,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
(data) => {
|
||||
if (this.assign && data.assignmentId == this.assign.id && data.userId == this.currentUserId) {
|
||||
// Assignment submitted, check completion.
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
|
||||
// Reload data since it can have offline data now.
|
||||
this.showLoadingAndRefresh(true, false);
|
||||
|
@ -140,7 +140,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
|
||||
try {
|
||||
await AddonModAssign.logView(this.assign!.id, this.assign!.name);
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
} catch {
|
||||
// Ignore errors. Just don't check Module completion.
|
||||
}
|
||||
|
@ -164,11 +164,11 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
if (this.assign && (this.description || this.assign.introattachments)) {
|
||||
CoreTextUtils.viewText(Translate.instant('core.description'), this.description || '', {
|
||||
component: this.component,
|
||||
componentId: this.module!.id,
|
||||
componentId: this.module.id,
|
||||
files: this.assign.introattachments,
|
||||
filter: true,
|
||||
contextLevel: 'module',
|
||||
instanceId: this.module!.id,
|
||||
instanceId: this.module.id,
|
||||
courseId: this.courseId,
|
||||
});
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
|
||||
// Get assignment data.
|
||||
try {
|
||||
this.assign = await AddonModAssign.getAssignment(this.courseId!, this.module!.id);
|
||||
this.assign = await AddonModAssign.getAssignment(this.courseId, this.module.id);
|
||||
|
||||
this.dataRetrieved.emit(this.assign);
|
||||
this.description = this.assign.intro;
|
||||
|
@ -200,7 +200,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
this.hasOffline = await AddonModAssignOffline.hasAssignOfflineData(this.assign.id);
|
||||
|
||||
// Get assignment submissions.
|
||||
const submissions = await AddonModAssign.getSubmissions(this.assign.id, { cmId: this.module!.id });
|
||||
const submissions = await AddonModAssign.getSubmissions(this.assign.id, { cmId: this.module.id });
|
||||
const time = CoreTimeUtils.timestamp();
|
||||
|
||||
this.canViewAllSubmissions = submissions.canviewsubmissions;
|
||||
|
@ -244,7 +244,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
|
||||
try {
|
||||
// Check if the user can view their own submission.
|
||||
await AddonModAssign.getSubmissionStatus(this.assign.id, { cmId: this.module!.id });
|
||||
await AddonModAssign.getSubmissionStatus(this.assign.id, { cmId: this.module.id });
|
||||
this.canViewOwnSubmission = true;
|
||||
} catch (error) {
|
||||
this.canViewOwnSubmission = false;
|
||||
|
@ -269,7 +269,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
|
||||
const submissionStatus = await AddonModAssign.getSubmissionStatus(this.assign!.id, {
|
||||
groupId: this.group,
|
||||
cmId: this.module!.id,
|
||||
cmId: this.module.id,
|
||||
});
|
||||
|
||||
this.summary = submissionStatus.gradingsummary;
|
||||
|
@ -345,7 +345,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
protected async invalidateContent(): Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
promises.push(AddonModAssign.invalidateAssignmentData(this.courseId!));
|
||||
promises.push(AddonModAssign.invalidateAssignmentData(this.courseId));
|
||||
|
||||
if (this.assign) {
|
||||
promises.push(AddonModAssign.invalidateAllSubmissionData(this.assign.id));
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
<ion-content>
|
||||
<core-split-view>
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded || !submissions.loaded" (ionRefresh)="refreshList($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded || !submissions.loaded" (ionRefresh)="refreshList($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="loaded && submissions.loaded">
|
||||
|
|
|
@ -328,9 +328,9 @@ export class AddonModAssignSubmissionListPage implements AfterViewInit, OnDestro
|
|||
*
|
||||
* @param refresher Refresher.
|
||||
*/
|
||||
refreshList(refresher?: CustomEvent<IonRefresher>): void {
|
||||
refreshList(refresher?: IonRefresher): void {
|
||||
this.refreshAllData(true).finally(() => {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</ion-header>
|
||||
<ion-content>
|
||||
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshSubmission($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshSubmission($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="loaded">
|
||||
|
|
|
@ -155,9 +155,9 @@ export class AddonModAssignSubmissionReviewPage implements OnInit, CanLeave {
|
|||
*
|
||||
* @param refresher Refresher.
|
||||
*/
|
||||
refreshSubmission(refresher?: CustomEvent<IonRefresher>): void {
|
||||
refreshSubmission(refresher?: IonRefresher): void {
|
||||
this.refreshAllData().finally(() => {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||
|
||||
<core-course-module-description [description]="description" [component]="component" [componentId]="componentId"
|
||||
contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId"></core-course-module-description>
|
||||
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-course-module-description>
|
||||
|
||||
<ion-card class="core-warning-card" *ngIf="warning">
|
||||
<ion-icon name="fas-exclamation-triangle" slot="start"></ion-icon>
|
||||
|
@ -38,7 +38,7 @@
|
|||
</core-navigation-bar>
|
||||
|
||||
<core-format-text [component]="component" [componentId]="componentId" [text]="chapterContent" contextLevel="module"
|
||||
[contextInstanceId]="module?.id" [courseId]="courseId"></core-format-text>
|
||||
[contextInstanceId]="module.id" [courseId]="courseId"></core-format-text>
|
||||
<div class="ion-margin-top" *ngIf="tagsEnabled && tags?.length > 0">
|
||||
<strong>{{ 'core.tag.tags' | translate }}: </strong>
|
||||
<core-tag-list [tags]="tags"></core-tag-list>
|
||||
|
|
|
@ -87,7 +87,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
|||
const modal = await ModalController.create({
|
||||
component: AddonModBookTocComponent,
|
||||
componentProps: {
|
||||
moduleId: this.module!.id,
|
||||
moduleId: this.module.id,
|
||||
chapters: this.chapters,
|
||||
selected: this.currentChapter,
|
||||
courseId: this.courseId,
|
||||
|
@ -129,7 +129,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
|||
* @return Resolved when done.
|
||||
*/
|
||||
protected invalidateContent(): Promise<void> {
|
||||
return AddonModBook.invalidateContent(this.module!.id, this.courseId!);
|
||||
return AddonModBook.invalidateContent(this.module.id, this.courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,7 +143,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
|||
let downloadResult: CoreCourseResourceDownloadResult | undefined;
|
||||
|
||||
// Try to get the book data. Ignore errors since this WS isn't available in some Moodle versions.
|
||||
promises.push(CoreUtils.ignoreErrors(AddonModBook.getBook(this.courseId!, this.module!.id))
|
||||
promises.push(CoreUtils.ignoreErrors(AddonModBook.getBook(this.courseId, this.module.id))
|
||||
.then((book) => {
|
||||
if (!book) {
|
||||
return;
|
||||
|
@ -169,8 +169,8 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
|||
try {
|
||||
await Promise.all(promises);
|
||||
|
||||
this.contentsMap = AddonModBook.getContentsMap(this.module!.contents);
|
||||
this.chapters = AddonModBook.getTocList(this.module!.contents);
|
||||
this.contentsMap = AddonModBook.getContentsMap(this.module.contents);
|
||||
this.chapters = AddonModBook.getTocList(this.module.contents);
|
||||
|
||||
if (typeof this.currentChapter == 'undefined' && typeof this.initialChapterId != 'undefined' && this.chapters) {
|
||||
// Initial chapter set. Validate that the chapter exists.
|
||||
|
@ -211,7 +211,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
|||
this.content?.scrollToTop();
|
||||
|
||||
try {
|
||||
const content = await AddonModBook.getChapterContent(this.contentsMap, chapterId, this.module!.id);
|
||||
const content = await AddonModBook.getChapterContent(this.contentsMap, chapterId, this.module.id);
|
||||
|
||||
this.tags = this.tagsEnabled ? this.contentsMap[this.currentChapter].tags : [];
|
||||
|
||||
|
@ -228,14 +228,14 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
|||
|
||||
// Chapter loaded, log view. We don't return the promise because we don't want to block the user for this.
|
||||
await CoreUtils.ignoreErrors(AddonModBook.logView(
|
||||
this.module!.instance!,
|
||||
this.module.instance!,
|
||||
logChapterId ? chapterId : undefined,
|
||||
this.module!.name,
|
||||
this.module.name,
|
||||
));
|
||||
|
||||
// Module is completed when last chapter is viewed, so we only check completion if the last is reached.
|
||||
if (!this.nextChapter) {
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
}
|
||||
} catch (error) {
|
||||
CoreDomUtils.showErrorModalDefault(error, 'addon.mod_book.errorchapter', true);
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||
|
||||
<core-course-module-description [description]="description" [component]="component" [componentId]="componentId"
|
||||
contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
|
||||
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-course-module-description>
|
||||
|
||||
<ion-list *ngIf="subfolder && (subfolder!.files.length + subfolder!.folders.length > 0)">
|
||||
|
|
|
@ -55,7 +55,7 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
|
|||
this.canGetFolder = AddonModFolder.isGetFolderWSAvailable();
|
||||
|
||||
if (this.subfolder) {
|
||||
this.description = this.folderInstance ? this.folderInstance.intro : this.module!.description;
|
||||
this.description = this.folderInstance ? this.folderInstance.intro : this.module.description;
|
||||
|
||||
this.loaded = true;
|
||||
this.refreshIcon = CoreConstants.ICON_REFRESH;
|
||||
|
@ -67,8 +67,8 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
|
|||
await this.loadContent();
|
||||
|
||||
try {
|
||||
await AddonModFolder.logView(this.module!.instance!, this.module!.name);
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
await AddonModFolder.logView(this.module.instance!, this.module.name);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
} catch {
|
||||
// Ignore errors.
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
|
|||
* @return Resolved when done.
|
||||
*/
|
||||
protected async invalidateContent(): Promise<void> {
|
||||
await AddonModFolder.invalidateContent(this.module!.id, this.courseId!);
|
||||
await AddonModFolder.invalidateContent(this.module.id, this.courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,22 +96,22 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
|
|||
protected async fetchContent(refresh = false): Promise<void> {
|
||||
try {
|
||||
if (this.canGetFolder) {
|
||||
this.folderInstance = await AddonModFolder.getFolder(this.courseId!, this.module!.id);
|
||||
await CoreCourse.loadModuleContents(this.module!, this.courseId, undefined, false, refresh);
|
||||
this.folderInstance = await AddonModFolder.getFolder(this.courseId, this.module.id);
|
||||
await CoreCourse.loadModuleContents(this.module, this.courseId, undefined, false, refresh);
|
||||
} else {
|
||||
const module = await CoreCourse.getModule(this.module!.id, this.courseId);
|
||||
const module = await CoreCourse.getModule(this.module.id, this.courseId);
|
||||
|
||||
if (!module.contents.length && this.module!.contents.length && !CoreApp.isOnline()) {
|
||||
if (!module.contents.length && this.module.contents.length && !CoreApp.isOnline()) {
|
||||
// The contents might be empty due to a cached data. Use the old ones.
|
||||
module.contents = this.module!.contents;
|
||||
module.contents = this.module.contents;
|
||||
}
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
this.dataRetrieved.emit(this.folderInstance || this.module);
|
||||
|
||||
this.description = this.folderInstance ? this.folderInstance.intro : this.module!.description;
|
||||
this.subfolder = AddonModFolderHelper.formatContents(this.module!.contents);
|
||||
this.description = this.folderInstance ? this.folderInstance.intro : this.module.description;
|
||||
this.subfolder = AddonModFolderHelper.formatContents(this.module.contents);
|
||||
} finally {
|
||||
this.fillContextMenu(refresh);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="subfolder || !activityComponent?.loaded"
|
||||
(ionRefresh)="activityComponent?.doRefresh($event)">
|
||||
(ionRefresh)="activityComponent?.doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
<!-- Content. -->
|
||||
<core-split-view>
|
||||
<ion-refresher slot="fixed" [disabled]="!discussions.loaded" (ionRefresh)="doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!discussions.loaded" (ionRefresh)="doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
this.eventReceived.bind(this, false),
|
||||
);
|
||||
this.changeDiscObserver = CoreEvents.on(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data => {
|
||||
if ((this.forum && this.forum.id === data.forumId) || data.cmId === this.module!.id) {
|
||||
if ((this.forum && this.forum.id === data.forumId) || data.cmId === this.module.id) {
|
||||
AddonModForum.invalidateDiscussionsList(this.forum!.id).finally(() => {
|
||||
if (data.discussionId) {
|
||||
// Discussion changed, search it in the list of discussions.
|
||||
|
@ -198,7 +198,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
AddonModForum.instance
|
||||
.logView(this.forum.id, this.forum.name)
|
||||
.then(async () => {
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
|
||||
return;
|
||||
}),
|
||||
|
@ -324,7 +324,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
|
||||
promises.push(
|
||||
AddonModForum.instance
|
||||
.getAccessInformation(this.forum.id, { cmId: this.module!.id })
|
||||
.getAccessInformation(this.forum.id, { cmId: this.module.id })
|
||||
.then(async accessInfo => {
|
||||
// Disallow adding discussions if cut-off date is reached and the user has not the
|
||||
// capability to override it.
|
||||
|
@ -341,7 +341,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
// Use the canAddDiscussion WS to check if the user can pin discussions.
|
||||
promises.push(
|
||||
AddonModForum.instance
|
||||
.canAddDiscussionToAll(this.forum.id, { cmId: this.module!.id })
|
||||
.canAddDiscussionToAll(this.forum.id, { cmId: this.module.id })
|
||||
.then(async response => {
|
||||
this.canPin = !!response.canpindiscussions;
|
||||
|
||||
|
@ -525,7 +525,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
protected async invalidateContent(): Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
promises.push(AddonModForum.invalidateForumData(this.courseId!));
|
||||
promises.push(AddonModForum.invalidateForumData(this.courseId));
|
||||
|
||||
if (this.forum) {
|
||||
promises.push(AddonModForum.invalidateDiscussionsList(this.forum.id));
|
||||
|
@ -546,7 +546,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected sync(): Promise<AddonModForumSyncResult> {
|
||||
return AddonModForumPrefetchHandler.sync(this.module!, this.courseId!);
|
||||
return AddonModForumPrefetchHandler.sync(this.module, this.courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -582,7 +582,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
isNewDiscussion: boolean,
|
||||
data: AddonModForumNewDiscussionData | AddonModForumReplyDiscussionData,
|
||||
): void {
|
||||
if ((this.forum && this.forum.id === data.forumId) || data.cmId === this.module?.id) {
|
||||
if ((this.forum && this.forum.id === data.forumId) || data.cmId === this.module.id) {
|
||||
this.showLoadingAndRefresh(false).finally(() => {
|
||||
// If it's a new discussion in tablet mode, try to open it.
|
||||
if (isNewDiscussion && CoreScreen.isTablet) {
|
||||
|
@ -606,7 +606,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
});
|
||||
|
||||
// Check completion since it could be configured to complete once the user adds a new discussion or replies.
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -668,7 +668,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
componentProps: {
|
||||
discussion,
|
||||
forumId: this.forum!.id,
|
||||
cmId: this.module!.id,
|
||||
cmId: this.module.id,
|
||||
},
|
||||
event,
|
||||
});
|
||||
|
@ -733,7 +733,7 @@ class AddonModForumDiscussionsManager extends CorePageItemsListManager<Discussio
|
|||
getItemQueryParams(discussion: DiscussionItem): Params {
|
||||
return {
|
||||
courseId: this.component.courseId,
|
||||
cmId: this.component.module!.id,
|
||||
cmId: this.component.module.id,
|
||||
forumId: this.component.forum!.id,
|
||||
...(this.isOnlineDiscussion(discussion) ? { discussion, trackPosts: this.component.trackPosts } : {}),
|
||||
};
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!discussionLoaded" (ionRefresh)="doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!discussionLoaded" (ionRefresh)="doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import { CoreRatingOffline } from '@features/rating/services/rating-offline';
|
|||
import { CoreRatingSyncProvider } from '@features/rating/services/rating-sync';
|
||||
import { CoreUser } from '@features/user/services/user';
|
||||
import { CanLeave } from '@guards/can-leave';
|
||||
import { IonContent } from '@ionic/angular';
|
||||
import { IonContent, IonRefresher } from '@ionic/angular';
|
||||
import { CoreApp } from '@services/app';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CoreScreen } from '@services/screen';
|
||||
|
@ -612,10 +612,10 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
|
|||
* @param showErrors If show errors to the user of hide them.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async doRefresh(refresher?: any, done?: () => void, showErrors: boolean = false): Promise<void> {
|
||||
async doRefresh(refresher?: IonRefresher | null, done?: () => void, showErrors: boolean = false): Promise<void> {
|
||||
if (this.discussionLoaded) {
|
||||
await this.refreshPosts(true, showErrors).finally(() => {
|
||||
refresher && refresher.complete();
|
||||
refresher?.complete();
|
||||
done && done();
|
||||
});
|
||||
}
|
||||
|
@ -651,7 +651,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
|
|||
* @param type Sort type.
|
||||
* @return Promised resolved when done.
|
||||
*/
|
||||
changeSort(type: SortType): Promise<any> {
|
||||
changeSort(type: SortType): Promise<void> {
|
||||
this.discussionLoaded = false;
|
||||
this.sort = type;
|
||||
CoreSites.getCurrentSite()!.setLocalSiteConfig('AddonModForumDiscussionSort', this.sort);
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<div class="addon-mod-imscp-container">
|
||||
<core-navigation-bar [previous]="previousItem" [next]="nextItem" (action)="loadItem($event)" [info]="description"
|
||||
[title]="'core.description' | translate" [component]="component" [componentId]="componentId" contextLevel="module"
|
||||
[contextInstanceId]="module?.id" [courseId]="courseId">
|
||||
[contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-navigation-bar>
|
||||
<core-iframe [src]="src"></core-iframe>
|
||||
</div>
|
||||
|
|
|
@ -59,8 +59,8 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom
|
|||
await this.loadContent();
|
||||
|
||||
try {
|
||||
await AddonModImscp.logView(this.module!.instance!, this.module!.name);
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
await AddonModImscp.logView(this.module.instance!, this.module.name);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
} catch {
|
||||
// Ignore errors.
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom
|
|||
* @return Resolved when done.
|
||||
*/
|
||||
protected async invalidateContent(): Promise<void> {
|
||||
await AddonModImscp.invalidateContent(this.module!.id, this.courseId!);
|
||||
await AddonModImscp.invalidateContent(this.module.id, this.courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,7 +85,7 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom
|
|||
let downloadResult: CoreCourseResourceDownloadResult;
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
promises.push(AddonModImscp.getImscp(this.courseId!, this.module!.id).then((imscp) => {
|
||||
promises.push(AddonModImscp.getImscp(this.courseId, this.module.id).then((imscp) => {
|
||||
this.description = imscp.intro;
|
||||
this.dataRetrieved.emit(imscp);
|
||||
|
||||
|
@ -101,7 +101,7 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom
|
|||
try {
|
||||
await Promise.all(promises);
|
||||
|
||||
this.items = AddonModImscp.createItemList(this.module!.contents);
|
||||
this.items = AddonModImscp.createItemList(this.module.contents);
|
||||
|
||||
if (this.items.length && typeof this.currentItem == 'undefined') {
|
||||
this.currentItem = this.items[0].href;
|
||||
|
@ -129,7 +129,7 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
async loadItem(itemId?: string): Promise<void> {
|
||||
const src = await AddonModImscp.getIframeSrc(this.module!, itemId);
|
||||
const src = await AddonModImscp.getIframeSrc(this.module, itemId);
|
||||
this.currentItem = itemId;
|
||||
this.previousItem = itemId ? AddonModImscp.getPreviousItem(this.items, itemId) : '';
|
||||
this.nextItem = itemId ? AddonModImscp.getNextItem(this.items, itemId) : '';
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
<core-tab [title]="'addon.mod_lesson.preview' | translate" (ionSelect)="indexSelected()">
|
||||
<ng-template>
|
||||
<core-course-module-description [description]="description" [component]="component" [componentId]="componentId"
|
||||
contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
|
||||
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-course-module-description>
|
||||
|
||||
<!-- Prevent access messages. Only show the first one. -->
|
||||
|
|
|
@ -146,7 +146,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
let lessonReady = true;
|
||||
this.askPassword = false;
|
||||
|
||||
this.lesson = await AddonModLesson.getLesson(this.courseId!, this.module!.id);
|
||||
this.lesson = await AddonModLesson.getLesson(this.courseId, this.module.id);
|
||||
|
||||
this.dataRetrieved.emit(this.lesson);
|
||||
this.description = this.lesson.intro; // Show description only if intro is present.
|
||||
|
@ -156,7 +156,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
await this.syncActivity(showErrors);
|
||||
}
|
||||
|
||||
this.accessInfo = await AddonModLesson.getAccessInformation(this.lesson.id, { cmId: this.module!.id });
|
||||
this.accessInfo = await AddonModLesson.getAccessInformation(this.lesson.id, { cmId: this.module.id });
|
||||
this.canManage = this.accessInfo.canmanage;
|
||||
this.canViewReports = this.accessInfo.canviewreports;
|
||||
this.preventReasons = [];
|
||||
|
@ -227,7 +227,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
}
|
||||
|
||||
const promises: Promise<unknown>[] = [];
|
||||
const options = { cmId: this.module!.id };
|
||||
const options = { cmId: this.module.id };
|
||||
|
||||
// Check if there is offline data.
|
||||
promises.push(AddonModLessonSync.hasDataToSync(this.lesson.id, this.accessInfo.attemptscount).then((hasData) => {
|
||||
|
@ -293,7 +293,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
protected hasSyncSucceed(result: AddonModLessonSyncResult): boolean {
|
||||
if (result.updated || this.dataSent) {
|
||||
// Check completion status if something was sent.
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
}
|
||||
|
||||
this.dataSent = false;
|
||||
|
@ -339,7 +339,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
protected async invalidateContent(): Promise<void> {
|
||||
const promises: Promise<unknown>[] = [];
|
||||
|
||||
promises.push(AddonModLesson.invalidateLessonData(this.courseId!));
|
||||
promises.push(AddonModLesson.invalidateLessonData(this.courseId));
|
||||
|
||||
if (this.lesson) {
|
||||
promises.push(AddonModLesson.invalidateAccessInformation(this.lesson.id));
|
||||
|
@ -394,7 +394,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
AddonModLesson.logViewLesson(this.lesson.id, this.password, this.lesson.name),
|
||||
);
|
||||
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -414,7 +414,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
if (this.hasOffline) {
|
||||
if (continueLast) {
|
||||
pageId = await AddonModLesson.getLastPageSeen(this.lesson.id, this.accessInfo.attemptscount, {
|
||||
cmId: this.module!.id,
|
||||
cmId: this.module.id,
|
||||
});
|
||||
} else {
|
||||
pageId = this.accessInfo.firstpageid;
|
||||
|
@ -589,7 +589,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
this.showSpinner = true;
|
||||
|
||||
try {
|
||||
await AddonModLessonPrefetchHandler.prefetch(this.module!, this.courseId, true);
|
||||
await AddonModLessonPrefetchHandler.prefetch(this.module, this.courseId, true);
|
||||
|
||||
// Success downloading, open lesson.
|
||||
this.playLesson(continueLast);
|
||||
|
@ -661,8 +661,8 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
// The user sent data to server, but not in the sync process. Check if we need to fetch data.
|
||||
await CoreUtils.ignoreErrors(AddonModLessonSync.prefetchAfterUpdate(
|
||||
AddonModLessonPrefetchHandler.instance,
|
||||
this.module!,
|
||||
this.courseId!,
|
||||
this.module,
|
||||
this.courseId,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -677,7 +677,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
*/
|
||||
protected async validatePassword(password: string): Promise<void> {
|
||||
try {
|
||||
this.lesson = await AddonModLesson.getLessonWithPassword(this.lesson!.id, { password, cmId: this.module!.id });
|
||||
this.lesson = await AddonModLesson.getLessonWithPassword(this.lesson!.id, { password, cmId: this.module.id });
|
||||
|
||||
this.password = password;
|
||||
} catch (error) {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -98,9 +98,9 @@ export class AddonModLessonUserRetakePage implements OnInit {
|
|||
*
|
||||
* @param refresher Refresher.
|
||||
*/
|
||||
doRefresh(refresher: CustomEvent<IonRefresher>): void {
|
||||
doRefresh(refresher: IonRefresher): void {
|
||||
this.refreshData().finally(() => {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
// (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 { CoreSharedModule } from '@/core/shared.module';
|
||||
import { CoreCourseComponentsModule } from '@features/course/components/components.module';
|
||||
import { AddonModLtiIndexComponent } from './index';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonModLtiIndexComponent,
|
||||
],
|
||||
imports: [
|
||||
CoreSharedModule,
|
||||
CoreCourseComponentsModule,
|
||||
],
|
||||
exports: [
|
||||
AddonModLtiIndexComponent,
|
||||
],
|
||||
})
|
||||
export class AddonModLtiComponentsModule {}
|
|
@ -0,0 +1,31 @@
|
|||
<!-- Buttons to add to the header. -->
|
||||
<core-navbar-buttons slot="end">
|
||||
<core-context-menu>
|
||||
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate"
|
||||
[href]="externalUrl" iconAction="fas-external-link-alt">
|
||||
</core-context-menu-item>
|
||||
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate"
|
||||
(action)="expandDescription()" iconAction="fas-arrow-right">
|
||||
</core-context-menu-item>
|
||||
<core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}"
|
||||
iconAction="far-newspaper" (action)="gotoBlog()">
|
||||
</core-context-menu-item>
|
||||
<core-context-menu-item *ngIf="loaded && isOnline" [priority]="700" [content]="'core.refresh' | translate"
|
||||
(action)="doRefresh(null, $event)" [iconAction]="refreshIcon" [closeOnClick]="false">
|
||||
</core-context-menu-item>
|
||||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
|
||||
<!-- Content. -->
|
||||
<core-loading [hideUntil]="loaded" class="core-loading-center safe-area-page">
|
||||
<core-course-module-description *ngIf="lti && lti.showdescriptionlaunch" [description]="description" [component]="component"
|
||||
[componentId]="componentId" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-course-module-description>
|
||||
|
||||
<div class="ion-padding">
|
||||
<ion-button expand="block" (click)="launch()">
|
||||
<ion-icon name="fas-external-link-alt" slot="start"></ion-icon>
|
||||
{{ 'addon.mod_lti.launchactivity' | translate }}
|
||||
</ion-button>
|
||||
</div>
|
||||
</core-loading>
|
|
@ -0,0 +1,90 @@
|
|||
// (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, Optional, OnInit } from '@angular/core';
|
||||
import { IonContent } from '@ionic/angular';
|
||||
|
||||
import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component';
|
||||
import { CoreCourseContentsPage } from '@features/course/pages/contents/contents';
|
||||
import { AddonModLti, AddonModLtiLti, AddonModLtiProvider } from '../../services/lti';
|
||||
import { AddonModLtiHelper } from '../../services/lti-helper';
|
||||
|
||||
/**
|
||||
* Component that displays an LTI entry page.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'addon-mod-lti-index',
|
||||
templateUrl: 'addon-mod-lti-index.html',
|
||||
})
|
||||
export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit {
|
||||
|
||||
component = AddonModLtiProvider.COMPONENT;
|
||||
moduleName = 'lti';
|
||||
|
||||
lti?: AddonModLtiLti; // The LTI object.
|
||||
|
||||
protected fetchContentDefaultError = 'addon.mod_lti.errorgetlti';
|
||||
|
||||
constructor(
|
||||
protected content?: IonContent,
|
||||
@Optional() courseContentsPage?: CoreCourseContentsPage,
|
||||
) {
|
||||
super('AddonModLtiIndexComponent', content, courseContentsPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
super.ngOnInit();
|
||||
|
||||
this.loadContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected async fetchContent(refresh: boolean = false): Promise<void> {
|
||||
try {
|
||||
this.lti = await AddonModLti.getLti(this.courseId, this.module.id);
|
||||
|
||||
this.description = this.lti.intro;
|
||||
this.dataRetrieved.emit(this.lti);
|
||||
} finally {
|
||||
this.fillContextMenu(refresh);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected async invalidateContent(): Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
promises.push(AddonModLti.invalidateLti(this.courseId));
|
||||
if (this.lti) {
|
||||
promises.push(AddonModLti.invalidateLtiLaunchData(this.lti.id));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the LTI.
|
||||
*/
|
||||
launch(): void {
|
||||
AddonModLtiHelper.getDataAndLaunch(this.courseId, this.module, this.lti);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"errorgetlti": "Error getting module data.",
|
||||
"errorinvalidlaunchurl": "The launch URL is not valid.",
|
||||
"launchactivity": "Launch the activity",
|
||||
"modulenameplural": "External tools"
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// (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 { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { AddonModLtiComponentsModule } from './components/components.module';
|
||||
import { AddonModLtiIndexPage } from './pages/index/index.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: ':courseId/:cmId',
|
||||
component: AddonModLtiIndexPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CoreSharedModule,
|
||||
AddonModLtiComponentsModule,
|
||||
],
|
||||
declarations: [
|
||||
AddonModLtiIndexPage,
|
||||
],
|
||||
})
|
||||
export class AddonModLtiLazyModule {}
|
|
@ -0,0 +1,60 @@
|
|||
// (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, Type } from '@angular/core';
|
||||
import { Routes } from '@angular/router';
|
||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
|
||||
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
|
||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
|
||||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
|
||||
import { AddonModLtiComponentsModule } from './components/components.module';
|
||||
import { AddonModLtiIndexLinkHandler } from './services/handlers/index-link';
|
||||
import { AddonModLtiListLinkHandler } from './services/handlers/list-link';
|
||||
import { AddonModLtiModuleHandler, AddonModLtiModuleHandlerService } from './services/handlers/module';
|
||||
import { AddonModLtiPrefetchHandler } from './services/handlers/prefetch';
|
||||
import { AddonModLtiProvider } from './services/lti';
|
||||
import { AddonModLtiHelperProvider } from './services/lti-helper';
|
||||
|
||||
export const ADDON_MOD_LTI_SERVICES: Type<unknown>[] = [
|
||||
AddonModLtiProvider,
|
||||
AddonModLtiHelperProvider,
|
||||
];
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: AddonModLtiModuleHandlerService.PAGE_NAME,
|
||||
loadChildren: () => import('./lti-lazy.module').then(m => m.AddonModLtiLazyModule),
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CoreMainMenuTabRoutingModule.forChild(routes),
|
||||
AddonModLtiComponentsModule,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
multi: true,
|
||||
deps: [],
|
||||
useFactory: () => () => {
|
||||
CoreCourseModuleDelegate.registerHandler(AddonModLtiModuleHandler.instance);
|
||||
CoreContentLinksDelegate.registerHandler(AddonModLtiIndexLinkHandler.instance);
|
||||
CoreContentLinksDelegate.registerHandler(AddonModLtiListLinkHandler.instance);
|
||||
CoreCourseModulePrefetchDelegate.registerHandler(AddonModLtiPrefetchHandler.instance);
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AddonModLtiModule {}
|
|
@ -0,0 +1,22 @@
|
|||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>
|
||||
<core-format-text [text]="title" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-format-text>
|
||||
</ion-title>
|
||||
|
||||
<ion-buttons slot="end">
|
||||
<!-- The buttons defined by the component will be added in here. -->
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
<addon-mod-lti-index [module]="module" [courseId]="courseId" (dataRetrieved)="updateData($event)"></addon-mod-lti-index>
|
||||
</ion-content>
|
|
@ -0,0 +1,31 @@
|
|||
// (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, ViewChild } from '@angular/core';
|
||||
|
||||
import { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page';
|
||||
import { AddonModLtiIndexComponent } from '../../components/index/index';
|
||||
|
||||
/**
|
||||
* Page that displays an LTI.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'page-addon-mod-lti-index',
|
||||
templateUrl: 'index.html',
|
||||
})
|
||||
export class AddonModLtiIndexPage extends CoreCourseModuleMainActivityPage<AddonModLtiIndexComponent> {
|
||||
|
||||
@ViewChild(AddonModLtiIndexComponent) activityComponent?: AddonModLtiIndexComponent;
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// (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 { CoreContentLinksModuleIndexHandler } from '@features/contentlinks/classes/module-index-handler';
|
||||
import { makeSingleton } from '@singletons';
|
||||
|
||||
/**
|
||||
* Handler to treat links to LTI.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AddonModLtiIndexLinkHandlerService extends CoreContentLinksModuleIndexHandler {
|
||||
|
||||
name = 'AddonModLtiIndexLinkHandlerService';
|
||||
|
||||
constructor() {
|
||||
super('AddonModLti', 'lti', 'l');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const AddonModLtiIndexLinkHandler = makeSingleton(AddonModLtiIndexLinkHandlerService);
|
|
@ -0,0 +1,33 @@
|
|||
// (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 { CoreContentLinksModuleListHandler } from '@features/contentlinks/classes/module-list-handler';
|
||||
import { makeSingleton } from '@singletons';
|
||||
|
||||
/**
|
||||
* Handler to treat links to LTI list page.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AddonModLtiListLinkHandlerService extends CoreContentLinksModuleListHandler {
|
||||
|
||||
name = 'AddonModLtiListLinkHandler';
|
||||
|
||||
constructor() {
|
||||
super('AddonModLti', 'lti');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const AddonModLtiListLinkHandler = makeSingleton(AddonModLtiListLinkHandlerService);
|
|
@ -0,0 +1,145 @@
|
|||
// (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 { DomSanitizer } from '@angular/platform-browser';
|
||||
|
||||
import { CoreConstants } from '@/core/constants';
|
||||
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
|
||||
import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course';
|
||||
import { CoreCourseModule } from '@features/course/services/course-helper';
|
||||
import { CoreApp } from '@services/app';
|
||||
import { CoreFilepool } from '@services/filepool';
|
||||
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { AddonModLtiHelper } from '../lti-helper';
|
||||
import { AddonModLti, AddonModLtiProvider } from '../lti';
|
||||
import { AddonModLtiIndexComponent } from '../../components/index';
|
||||
|
||||
/**
|
||||
* Handler to support LTI modules.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AddonModLtiModuleHandlerService implements CoreCourseModuleHandler {
|
||||
|
||||
static readonly PAGE_NAME = 'mod_lti';
|
||||
|
||||
name = 'AddonModLti';
|
||||
modName = 'lti';
|
||||
|
||||
supportedFeatures = {
|
||||
[CoreConstants.FEATURE_GROUPS]: false,
|
||||
[CoreConstants.FEATURE_GROUPINGS]: false,
|
||||
[CoreConstants.FEATURE_MOD_INTRO]: true,
|
||||
[CoreConstants.FEATURE_COMPLETION_TRACKS_VIEWS]: true,
|
||||
[CoreConstants.FEATURE_GRADE_HAS_GRADE]: true,
|
||||
[CoreConstants.FEATURE_GRADE_OUTCOMES]: true,
|
||||
[CoreConstants.FEATURE_BACKUP_MOODLE2]: true,
|
||||
[CoreConstants.FEATURE_SHOW_DESCRIPTION]: true,
|
||||
};
|
||||
|
||||
constructor(protected sanitizer: DomSanitizer) {}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async isEnabled(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getData(
|
||||
module: CoreCourseAnyModuleData,
|
||||
courseId: number,
|
||||
): CoreCourseModuleHandlerData {
|
||||
|
||||
const data: CoreCourseModuleHandlerData = {
|
||||
icon: CoreCourse.getModuleIconSrc(this.modName, 'modicon' in module ? module.modicon : undefined),
|
||||
title: module.name,
|
||||
class: 'addon-mod_lti-handler',
|
||||
action(event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions): void {
|
||||
options = options || {};
|
||||
options.params = options.params || {};
|
||||
Object.assign(options.params, { module });
|
||||
const routeParams = '/' + courseId + '/' + module.id;
|
||||
|
||||
CoreNavigator.navigateToSitePath(AddonModLtiModuleHandlerService.PAGE_NAME + routeParams, options);
|
||||
},
|
||||
buttons: [{
|
||||
icon: 'link',
|
||||
label: 'addon.mod_lti.launchactivity',
|
||||
action: (event: Event, module: CoreCourseModule, courseId: number): void => {
|
||||
// Launch the LTI.
|
||||
AddonModLtiHelper.getDataAndLaunch(courseId, module);
|
||||
},
|
||||
}],
|
||||
};
|
||||
|
||||
// Handle custom icons.
|
||||
CoreUtils.ignoreErrors(this.loadCustomIcon(module, courseId, data));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the custom icon.
|
||||
*
|
||||
* @param module Module.
|
||||
* @param courseId Course ID.
|
||||
* @param data Handler data.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async loadCustomIcon(
|
||||
module: CoreCourseAnyModuleData,
|
||||
courseId: number,
|
||||
handlerData: CoreCourseModuleHandlerData,
|
||||
): Promise<void> {
|
||||
const lti = await AddonModLti.getLti(courseId, module.id);
|
||||
|
||||
const icon = lti.secureicon || lti.icon;
|
||||
if (!icon) {
|
||||
return;
|
||||
}
|
||||
|
||||
const siteId = CoreSites.getCurrentSiteId();
|
||||
|
||||
try {
|
||||
await CoreFilepool.downloadUrl(siteId, icon, false, AddonModLtiProvider.COMPONENT, module.id);
|
||||
|
||||
// Get the internal URL.
|
||||
const url = await CoreFilepool.getSrcByUrl(siteId, icon, AddonModLtiProvider.COMPONENT, module.id);
|
||||
|
||||
handlerData.icon = this.sanitizer.bypassSecurityTrustUrl(url);
|
||||
} catch {
|
||||
// Error downloading. If we're online we'll set the online url.
|
||||
if (CoreApp.isOnline()) {
|
||||
handlerData.icon = this.sanitizer.bypassSecurityTrustUrl(icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async getMainComponent(): Promise<Type<unknown> | undefined> {
|
||||
return AddonModLtiIndexComponent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const AddonModLtiModuleHandler = makeSingleton(AddonModLtiModuleHandlerService);
|
|
@ -0,0 +1,62 @@
|
|||
// (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 { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler';
|
||||
import { CoreCourseAnyModuleData } from '@features/course/services/course';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { AddonModLti, AddonModLtiProvider } from '../lti';
|
||||
|
||||
/**
|
||||
* Handler to prefetch LTIs. LTIs cannot be prefetched, but the handler will be used to invalidate some data on course PTR.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AddonModLtiPrefetchHandlerService extends CoreCourseActivityPrefetchHandlerBase {
|
||||
|
||||
name = 'AddonModLti';
|
||||
modName = 'lti';
|
||||
component = AddonModLtiProvider.COMPONENT;
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async download(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
invalidateModule(module: CoreCourseAnyModuleData, courseId: number): Promise<void> {
|
||||
return AddonModLti.invalidateLti(courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async isDownloadable(): Promise<boolean> {
|
||||
return false; // LTIs aren't downloadable.
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async prefetch(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const AddonModLtiPrefetchHandler = makeSingleton(AddonModLtiPrefetchHandlerService);
|
|
@ -0,0 +1,125 @@
|
|||
// (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 { CoreCourse } from '@features/course/services/course';
|
||||
import { CoreCourseModule } from '@features/course/services/course-helper';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { makeSingleton, Platform } from '@singletons';
|
||||
import { CoreEvents } from '@singletons/events';
|
||||
import { AddonModLti, AddonModLtiLti } from './lti';
|
||||
|
||||
/**
|
||||
* Service that provides some helper functions for LTI.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AddonModLtiHelperProvider {
|
||||
|
||||
protected pendingCheckCompletion: {[moduleId: string]: {courseId: number; module: CoreCourseModule}} = {};
|
||||
|
||||
constructor() {
|
||||
Platform.resume.subscribe(() => {
|
||||
// User went back to the app, check pending completions.
|
||||
for (const moduleId in this.pendingCheckCompletion) {
|
||||
const data = this.pendingCheckCompletion[moduleId];
|
||||
|
||||
CoreCourse.checkModuleCompletion(data.courseId, data.module.completiondata);
|
||||
}
|
||||
});
|
||||
|
||||
// Clear pending completion on logout.
|
||||
CoreEvents.on(CoreEvents.LOGOUT, () => {
|
||||
this.pendingCheckCompletion = {};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get needed data and launch the LTI.
|
||||
*
|
||||
* @param courseId Course ID.
|
||||
* @param module Module.
|
||||
* @param lti LTI instance. If not provided it will be obtained.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async getDataAndLaunch(courseId: number, module: CoreCourseModule, lti?: AddonModLtiLti, siteId?: string): Promise<void> {
|
||||
siteId = siteId || CoreSites.getCurrentSiteId();
|
||||
|
||||
const modal = await CoreDomUtils.showModalLoading();
|
||||
|
||||
try {
|
||||
const openInBrowser = await AddonModLti.isOpenInAppBrowserDisabled(siteId);
|
||||
|
||||
if (openInBrowser) {
|
||||
const site = await CoreSites.getSite(siteId);
|
||||
|
||||
// The view event is triggered by the browser, mark the module as pending to check completion.
|
||||
this.pendingCheckCompletion[module.id] = {
|
||||
courseId,
|
||||
module,
|
||||
};
|
||||
|
||||
return site.openInBrowserWithAutoLogin(module.url!);
|
||||
}
|
||||
|
||||
// Open in app.
|
||||
if (!lti) {
|
||||
lti = await AddonModLti.getLti(courseId, module.id);
|
||||
}
|
||||
|
||||
const launchData = await AddonModLti.getLtiLaunchData(lti.id);
|
||||
|
||||
// "View" LTI without blocking the UI.
|
||||
this.logViewAndCheckCompletion(courseId, module, lti.id, lti.name, siteId);
|
||||
|
||||
// Launch LTI.
|
||||
return AddonModLti.launch(launchData.endpoint, launchData.parameters);
|
||||
} catch (error) {
|
||||
CoreDomUtils.showErrorModalDefault(error, 'addon.mod_lti.errorgetlti', true);
|
||||
} finally {
|
||||
modal.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the LTI as being viewed and check completion.
|
||||
*
|
||||
* @param courseId Course ID.
|
||||
* @param module Module.
|
||||
* @param ltiId LTI id.
|
||||
* @param name Name of the lti.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async logViewAndCheckCompletion(
|
||||
courseId: number,
|
||||
module: CoreCourseModule,
|
||||
ltiId: number,
|
||||
name?: string,
|
||||
siteId?: string,
|
||||
): Promise<void> {
|
||||
try {
|
||||
await AddonModLti.logView(ltiId, name, siteId);
|
||||
|
||||
CoreCourse.checkModuleCompletion(courseId, module.completiondata);
|
||||
} catch (error) {
|
||||
// Ignore errors.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const AddonModLtiHelper = makeSingleton(AddonModLtiHelperProvider);
|
|
@ -0,0 +1,344 @@
|
|||
// (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 { CoreError } from '@classes/errors/error';
|
||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||
import { CoreCourseLogHelper } from '@features/course/services/log-helper';
|
||||
import { CoreApp } from '@services/app';
|
||||
import { CoreFile } from '@services/file';
|
||||
import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
import { CoreUrlUtils } from '@services/utils/url';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws';
|
||||
import { makeSingleton, Translate } from '@singletons';
|
||||
|
||||
const ROOT_CACHE_KEY = 'mmaModLti:';
|
||||
const LAUNCHER_FILE_NAME = 'lti_launcher.html';
|
||||
|
||||
/**
|
||||
* Service that provides some features for LTI.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AddonModLtiProvider {
|
||||
|
||||
static readonly COMPONENT = 'mmaModLti';
|
||||
|
||||
/**
|
||||
* Delete launcher.
|
||||
*
|
||||
* @return Promise resolved when the launcher file is deleted.
|
||||
*/
|
||||
deleteLauncher(): Promise<void> {
|
||||
return CoreFile.removeFile(LAUNCHER_FILE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a launcher file.
|
||||
*
|
||||
* @param url Launch URL.
|
||||
* @param params Launch params.
|
||||
* @return Promise resolved with the file URL.
|
||||
*/
|
||||
async generateLauncher(url: string, params: AddonModLtiParam[]): Promise<string> {
|
||||
if (!CoreFile.isAvailable()) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Generate a form with the params.
|
||||
let text = `<form action="${url}" name="ltiLaunchForm" method="post" encType="application/x-www-form-urlencoded">\n`;
|
||||
params.forEach((p) => {
|
||||
if (p.name == 'ext_submit') {
|
||||
text += ' <input type="submit"';
|
||||
} else {
|
||||
text += ' <input type="hidden" name="' + CoreTextUtils.escapeHTML(p.name) + '"';
|
||||
}
|
||||
text += ' value="' + CoreTextUtils.escapeHTML(p.value) + '"/>\n';
|
||||
});
|
||||
text += '</form>\n';
|
||||
|
||||
// Add an in-line script to automatically submit the form.
|
||||
text += '<script type="text/javascript"> \n' +
|
||||
' window.onload = function() { \n' +
|
||||
' document.ltiLaunchForm.submit(); \n' +
|
||||
' }; \n' +
|
||||
'</script> \n';
|
||||
|
||||
const entry = await CoreFile.writeFile(LAUNCHER_FILE_NAME, text);
|
||||
|
||||
return entry.toURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a LTI.
|
||||
*
|
||||
* @param courseId Course ID.
|
||||
* @param cmId Course module ID.
|
||||
* @param options Other options.
|
||||
* @return Promise resolved when the LTI is retrieved.
|
||||
*/
|
||||
async getLti(courseId: number, cmId: number, options: CoreSitesCommonWSOptions = {}): Promise<AddonModLtiLti> {
|
||||
const params: AddonModLtiGetLtisByCoursesWSParams = {
|
||||
courseids: [courseId],
|
||||
};
|
||||
const preSets: CoreSiteWSPreSets = {
|
||||
cacheKey: this.getLtiCacheKey(courseId),
|
||||
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
||||
component: AddonModLtiProvider.COMPONENT,
|
||||
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
|
||||
};
|
||||
|
||||
const site = await CoreSites.getSite(options.siteId);
|
||||
|
||||
const response = await site.read<AddonModLtiGetLtisByCoursesWSResponse>('mod_lti_get_ltis_by_courses', params, preSets);
|
||||
|
||||
const currentLti = response.ltis.find((lti) => lti.coursemodule == cmId);
|
||||
if (currentLti) {
|
||||
return currentLti;
|
||||
}
|
||||
|
||||
throw new CoreError('Activity not found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache key for LTI data WS calls.
|
||||
*
|
||||
* @param courseId Course ID.
|
||||
* @return Cache key.
|
||||
*/
|
||||
protected getLtiCacheKey(courseId: number): string {
|
||||
return ROOT_CACHE_KEY + 'lti:' + courseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a LTI launch data.
|
||||
*
|
||||
* @param id LTI id.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when the launch data is retrieved.
|
||||
*/
|
||||
async getLtiLaunchData(id: number, siteId?: string): Promise<AddonModLtiGetToolLaunchDataWSResponse> {
|
||||
const params: AddonModLtiGetToolLaunchDataWSParams = {
|
||||
toolid: id,
|
||||
};
|
||||
|
||||
// Try to avoid using cache since the "nonce" parameter is set to a timestamp.
|
||||
const preSets: CoreSiteWSPreSets = {
|
||||
getFromCache: false,
|
||||
saveToCache: true,
|
||||
emergencyCache: true,
|
||||
cacheKey: this.getLtiLaunchDataCacheKey(id),
|
||||
};
|
||||
|
||||
const site = await CoreSites.getSite(siteId);
|
||||
|
||||
return site.read<AddonModLtiGetToolLaunchDataWSResponse>('mod_lti_get_tool_launch_data', params, preSets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache key for LTI launch data WS calls.
|
||||
*
|
||||
* @param id LTI id.
|
||||
* @return Cache key.
|
||||
*/
|
||||
protected getLtiLaunchDataCacheKey(id: number): string {
|
||||
return `${ROOT_CACHE_KEY}launch:${id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates LTI data.
|
||||
*
|
||||
* @param courseId Course ID.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when the data is invalidated.
|
||||
*/
|
||||
async invalidateLti(courseId: number, siteId?: string): Promise<void> {
|
||||
const site = await CoreSites.getSite(siteId);
|
||||
|
||||
await site.invalidateWsCacheForKey(this.getLtiCacheKey(courseId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates options.
|
||||
*
|
||||
* @param id LTI id.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when the data is invalidated.
|
||||
*/
|
||||
async invalidateLtiLaunchData(id: number, siteId?: string): Promise<void> {
|
||||
const site = await CoreSites.getSite(siteId);
|
||||
|
||||
await site.invalidateWsCacheForKey(this.getLtiLaunchDataCacheKey(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if open in InAppBrowser is disabled.
|
||||
*
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with boolean: whether it's disabled.
|
||||
*/
|
||||
async isOpenInAppBrowserDisabled(siteId?: string): Promise<boolean> {
|
||||
const site = await CoreSites.getSite(siteId);
|
||||
|
||||
return this.isOpenInAppBrowserDisabledInSite(site);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if open in InAppBrowser is disabled.
|
||||
*
|
||||
* @param site Site. If not defined, current site.
|
||||
* @return Whether it's disabled.
|
||||
*/
|
||||
isOpenInAppBrowserDisabledInSite(site?: CoreSite): boolean {
|
||||
site = site || CoreSites.getCurrentSite();
|
||||
|
||||
return !!site?.isFeatureDisabled('CoreCourseModuleDelegate_AddonModLti:openInAppBrowser');
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch LTI.
|
||||
*
|
||||
* @param url Launch URL.
|
||||
* @param params Launch params.
|
||||
* @return Promise resolved when the WS call is successful.
|
||||
*/
|
||||
async launch(url: string, params: AddonModLtiParam[]): Promise<void> {
|
||||
if (!CoreUrlUtils.isHttpURL(url)) {
|
||||
throw Translate.instant('addon.mod_lti.errorinvalidlaunchurl');
|
||||
}
|
||||
|
||||
// Generate launcher and open it.
|
||||
const launcherUrl = await this.generateLauncher(url, params);
|
||||
|
||||
if (CoreApp.isMobile()) {
|
||||
CoreUtils.openInApp(launcherUrl);
|
||||
} else {
|
||||
// In desktop open in browser, we found some cases where inapp caused JS issues.
|
||||
CoreUtils.openInBrowser(launcherUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the LTI as being viewed.
|
||||
*
|
||||
* @param id LTI id.
|
||||
* @param name Name of the lti.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved when the WS call is successful.
|
||||
*/
|
||||
logView(id: number, name?: string, siteId?: string): Promise<any> {
|
||||
const params: AddonModLtiViewLtiWSParams = {
|
||||
ltiid: id,
|
||||
};
|
||||
|
||||
return CoreCourseLogHelper.logSingle(
|
||||
'mod_lti_view_lti',
|
||||
params,
|
||||
AddonModLtiProvider.COMPONENT,
|
||||
id,
|
||||
name,
|
||||
'lti',
|
||||
{},
|
||||
siteId,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const AddonModLti = makeSingleton(AddonModLtiProvider);
|
||||
|
||||
/**
|
||||
* Params of mod_lti_get_ltis_by_courses WS.
|
||||
*/
|
||||
export type AddonModLtiGetLtisByCoursesWSParams = {
|
||||
courseids?: number[]; // Array of course ids.
|
||||
};
|
||||
|
||||
/**
|
||||
* Data returned by mod_lti_get_ltis_by_courses WS.
|
||||
*/
|
||||
export type AddonModLtiGetLtisByCoursesWSResponse = {
|
||||
ltis: AddonModLtiLti[];
|
||||
warnings?: CoreWSExternalWarning[];
|
||||
};
|
||||
|
||||
/**
|
||||
* LTI returned by mod_lti_get_ltis_by_courses.
|
||||
*/
|
||||
export type AddonModLtiLti = {
|
||||
id: number; // External tool id.
|
||||
coursemodule: number; // Course module id.
|
||||
course: number; // Course id.
|
||||
name: string; // LTI name.
|
||||
intro?: string; // The LTI intro.
|
||||
introformat?: number; // Intro format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||
introfiles?: CoreWSExternalFile[]; // @since 3.2.
|
||||
timecreated?: number; // Time of creation.
|
||||
timemodified?: number; // Time of last modification.
|
||||
typeid?: number; // Type id.
|
||||
toolurl?: string; // Tool url.
|
||||
securetoolurl?: string; // Secure tool url.
|
||||
instructorchoicesendname?: string; // Instructor choice send name.
|
||||
instructorchoicesendemailaddr?: number; // Instructor choice send mail address.
|
||||
instructorchoiceallowroster?: number; // Instructor choice allow roster.
|
||||
instructorchoiceallowsetting?: number; // Instructor choice allow setting.
|
||||
instructorcustomparameters?: string; // Instructor custom parameters.
|
||||
instructorchoiceacceptgrades?: number; // Instructor choice accept grades.
|
||||
grade?: number; // Enable grades.
|
||||
launchcontainer?: number; // Launch container mode.
|
||||
resourcekey?: string; // Resource key.
|
||||
password?: string; // Shared secret.
|
||||
debuglaunch?: number; // Debug launch.
|
||||
showtitlelaunch?: number; // Show title launch.
|
||||
showdescriptionlaunch?: number; // Show description launch.
|
||||
servicesalt?: string; // Service salt.
|
||||
icon?: string; // Alternative icon URL.
|
||||
secureicon?: string; // Secure icon URL.
|
||||
section?: number; // Course section id.
|
||||
visible?: number; // Visible.
|
||||
groupmode?: number; // Group mode.
|
||||
groupingid?: number; // Group id.
|
||||
};
|
||||
|
||||
/**
|
||||
* Params of mod_lti_get_tool_launch_data WS.
|
||||
*/
|
||||
export type AddonModLtiGetToolLaunchDataWSParams = {
|
||||
toolid: number; // External tool instance id.
|
||||
};
|
||||
|
||||
/**
|
||||
* Data returned by mod_lti_get_tool_launch_data WS.
|
||||
*/
|
||||
export type AddonModLtiGetToolLaunchDataWSResponse = {
|
||||
endpoint: string; // Endpoint URL.
|
||||
parameters: AddonModLtiParam[];
|
||||
warnings?: CoreWSExternalWarning[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Param to send to the LTI.
|
||||
*/
|
||||
export type AddonModLtiParam = {
|
||||
name: string; // Parameter name.
|
||||
value: string; // Parameter value.
|
||||
};
|
||||
/**
|
||||
* Params of mod_lti_view_lti WS.
|
||||
*/
|
||||
export type AddonModLtiViewLtiWSParams = {
|
||||
ltiid: number; // Lti instance id.
|
||||
};
|
|
@ -25,6 +25,7 @@ import { AddonModPageModule } from './page/page.module';
|
|||
import { AddonModQuizModule } from './quiz/quiz.module';
|
||||
import { AddonModResourceModule } from './resource/resource.module';
|
||||
import { AddonModUrlModule } from './url/url.module';
|
||||
import { AddonModLtiModule } from './lti/lti.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
|
@ -40,6 +41,7 @@ import { AddonModUrlModule } from './url/url.module';
|
|||
AddonModResourceModule,
|
||||
AddonModFolderModule,
|
||||
AddonModImscpModule,
|
||||
AddonModLtiModule,
|
||||
],
|
||||
providers: [],
|
||||
exports: [],
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<core-loading [hideUntil]="loaded" class="core-loading-center safe-area-page">
|
||||
|
||||
<core-course-module-description *ngIf="displayDescription" [description]="description" [component]="component"
|
||||
[componentId]="componentId" contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
|
||||
[componentId]="componentId" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-course-module-description>
|
||||
|
||||
<ion-card class="core-warning-card" *ngIf="warning">
|
||||
|
@ -36,7 +36,7 @@
|
|||
|
||||
<div class="ion-padding">
|
||||
<core-format-text [component]="component" [componentId]="componentId" [text]="contents" contextLevel="module"
|
||||
[contextInstanceId]="module?.id" [courseId]="courseId">
|
||||
[contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-format-text>
|
||||
|
||||
<p class="ion-padding-bottom addon-mod_page-timemodified" *ngIf="displayTimemodified && timemodified">
|
||||
|
|
|
@ -59,8 +59,8 @@ export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComp
|
|||
await this.loadContent();
|
||||
|
||||
try {
|
||||
await AddonModPage.logView(this.module!.instance!, this.module!.name);
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
await AddonModPage.logView(this.module.instance!, this.module.name);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
} catch {
|
||||
// Ignore errors.
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComp
|
|||
* @return Resolved when done.
|
||||
*/
|
||||
protected async invalidateContent(): Promise<void> {
|
||||
await AddonModPage.invalidateContent(this.module!.id, this.courseId!);
|
||||
await AddonModPage.invalidateContent(this.module.id, this.courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,9 +92,9 @@ export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComp
|
|||
|
||||
// Get the module to get the latest title and description. Data should've been updated in download.
|
||||
if (this.canGetPage) {
|
||||
getPagePromise = AddonModPage.getPageData(this.courseId!, this.module!.id);
|
||||
getPagePromise = AddonModPage.getPageData(this.courseId, this.module.id);
|
||||
} else {
|
||||
getPagePromise = CoreCourse.getModule(this.module!.id, this.courseId!);
|
||||
getPagePromise = CoreCourse.getModule(this.module.id, this.courseId);
|
||||
}
|
||||
|
||||
promises.push(getPagePromise.then((page) => {
|
||||
|
@ -133,7 +133,7 @@ export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComp
|
|||
}));
|
||||
|
||||
// Get the page HTML.
|
||||
promises.push(AddonModPageHelper.getPageHtml(this.module!.contents, this.module!.id).then((content) => {
|
||||
promises.push(AddonModPageHelper.getPageHtml(this.module.contents, this.module.id).then((content) => {
|
||||
|
||||
this.contents = content;
|
||||
this.warning = downloadResult?.failed ? this.getErrorDownloadingSomeFilesMessage(downloadResult.error!) : '';
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<!-- Content. -->
|
||||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||
<core-course-module-description [description]="description" [component]="component" [componentId]="componentId"
|
||||
contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
|
||||
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-course-module-description>
|
||||
|
||||
<!-- Access rules description messages. -->
|
||||
|
@ -117,7 +117,7 @@
|
|||
<ion-label>
|
||||
<h3 class="item-heading">{{ 'addon.mod_quiz.comment' | translate }}</h3>
|
||||
<p><core-format-text [component]="component" [componentId]="componentId" [text]="gradebookFeedback"
|
||||
contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
|
||||
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-format-text></p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
@ -125,7 +125,7 @@
|
|||
<ion-label>
|
||||
<h3 class="item-heading">{{ 'addon.mod_quiz.overallfeedback' | translate }}</h3>
|
||||
<p><core-format-text [component]="component" [componentId]="componentId" [text]="overallFeedback"
|
||||
contextLevel="module" [contextInstanceId]="module?.id" [courseId]="courseId">
|
||||
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-format-text></p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
|
|
@ -129,7 +129,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
try {
|
||||
await AddonModQuiz.logViewQuiz(this.quiz.id, this.quiz.name);
|
||||
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
} catch {
|
||||
// Ignore errors.
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
this.showStatusSpinner = true;
|
||||
|
||||
try {
|
||||
await AddonModQuizPrefetchHandler.prefetch(this.module!, this.courseId, true);
|
||||
await AddonModQuizPrefetchHandler.prefetch(this.module, this.courseId, true);
|
||||
|
||||
// Success downloading, open quiz.
|
||||
this.openQuiz();
|
||||
|
@ -190,7 +190,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<void> {
|
||||
try {
|
||||
// First get the quiz instance.
|
||||
const quiz = await AddonModQuiz.getQuiz(this.courseId!, this.module!.id);
|
||||
const quiz = await AddonModQuiz.getQuiz(this.courseId, this.module.id);
|
||||
|
||||
this.gradeMethodReadable = AddonModQuiz.getQuizGradeMethod(quiz.grademethod);
|
||||
this.now = Date.now();
|
||||
|
@ -231,7 +231,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
}
|
||||
|
||||
// Get quiz access info.
|
||||
this.quizAccessInfo = await AddonModQuiz.getQuizAccessInformation(quiz.id, { cmId: this.module!.id });
|
||||
this.quizAccessInfo = await AddonModQuiz.getQuizAccessInformation(quiz.id, { cmId: this.module.id });
|
||||
|
||||
this.showReviewColumn = this.quizAccessInfo.canreviewmyattempts;
|
||||
this.accessRules = this.quizAccessInfo.accessrules;
|
||||
|
@ -242,7 +242,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
}
|
||||
|
||||
// Get question types in the quiz.
|
||||
const types = await AddonModQuiz.getQuizRequiredQtypes(quiz.id, { cmId: this.module!.id });
|
||||
const types = await AddonModQuiz.getQuizRequiredQtypes(quiz.id, { cmId: this.module.id });
|
||||
|
||||
this.unsupportedQuestions = AddonModQuiz.getUnsupportedQuestions(types);
|
||||
this.hasSupportedQuestions = !!types.find((type) => type != 'random' && this.unsupportedQuestions.indexOf(type) == -1);
|
||||
|
@ -265,10 +265,10 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
protected async getAttempts(quiz: AddonModQuizQuizData): Promise<void> {
|
||||
|
||||
// Get access information of last attempt (it also works if no attempts made).
|
||||
this.attemptAccessInfo = await AddonModQuiz.getAttemptAccessInformation(quiz.id, 0, { cmId: this.module!.id });
|
||||
this.attemptAccessInfo = await AddonModQuiz.getAttemptAccessInformation(quiz.id, 0, { cmId: this.module.id });
|
||||
|
||||
// Get attempts.
|
||||
const attempts = await AddonModQuiz.getUserAttempts(quiz.id, { cmId: this.module!.id });
|
||||
const attempts = await AddonModQuiz.getUserAttempts(quiz.id, { cmId: this.module.id });
|
||||
|
||||
this.attempts = await this.treatAttempts(quiz, attempts);
|
||||
|
||||
|
@ -386,7 +386,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
if (quiz.showFeedbackColumn) {
|
||||
// Get the quiz overall feedback.
|
||||
const response = await AddonModQuiz.getFeedbackForGrade(quiz.id, this.gradebookData.grade, {
|
||||
cmId: this.module!.id,
|
||||
cmId: this.module.id,
|
||||
});
|
||||
|
||||
this.overallFeedback = response.feedbacktext;
|
||||
|
@ -404,14 +404,14 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
}
|
||||
|
||||
// If we go to auto review it means an attempt was finished. Check completion status.
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
|
||||
// Verify that user can see the review.
|
||||
const attemptId = this.autoReview.attemptId;
|
||||
|
||||
if (this.quizAccessInfo?.canreviewmyattempts) {
|
||||
try {
|
||||
await AddonModQuiz.getAttemptReview(attemptId, { page: -1, cmId: this.module!.id });
|
||||
await AddonModQuiz.getAttemptReview(attemptId, { page: -1, cmId: this.module.id });
|
||||
|
||||
await CoreNavigator.navigate(`review/${attemptId}`);
|
||||
} catch {
|
||||
|
@ -429,7 +429,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
protected hasSyncSucceed(result: AddonModQuizSyncResult): boolean {
|
||||
if (result.attemptFinished) {
|
||||
// An attempt was finished, check completion status.
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
}
|
||||
|
||||
// If the sync call isn't rejected it means the sync was successful.
|
||||
|
@ -488,7 +488,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
protected async invalidateContent(): Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
promises.push(AddonModQuiz.invalidateQuizData(this.courseId!));
|
||||
promises.push(AddonModQuiz.invalidateQuizData(this.courseId));
|
||||
|
||||
if (this.quiz) {
|
||||
promises.push(AddonModQuiz.invalidateUserAttemptsForUser(this.quiz.id));
|
||||
|
@ -497,7 +497,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
promises.push(AddonModQuiz.invalidateAttemptAccessInformation(this.quiz.id));
|
||||
promises.push(AddonModQuiz.invalidateCombinedReviewOptionsForUser(this.quiz.id));
|
||||
promises.push(AddonModQuiz.invalidateUserBestGradeForUser(this.quiz.id));
|
||||
promises.push(AddonModQuiz.invalidateGradeFromGradebook(this.courseId!));
|
||||
promises.push(AddonModQuiz.invalidateGradeFromGradebook(this.courseId));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
|
@ -536,7 +536,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
|
||||
CoreNavigator.navigate('player', {
|
||||
params: {
|
||||
moduleUrl: this.module?.url,
|
||||
moduleUrl: this.module.url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -594,7 +594,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
}
|
||||
|
||||
// Get combined review options.
|
||||
promises.push(AddonModQuiz.getCombinedReviewOptions(quiz.id, { cmId: this.module!.id }).then((options) => {
|
||||
promises.push(AddonModQuiz.getCombinedReviewOptions(quiz.id, { cmId: this.module.id }).then((options) => {
|
||||
this.options = options;
|
||||
|
||||
return;
|
||||
|
@ -633,11 +633,11 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async getQuizGrade(quiz: AddonModQuizQuizData): Promise<void> {
|
||||
this.bestGrade = await AddonModQuiz.getUserBestGrade(quiz.id, { cmId: this.module!.id });
|
||||
this.bestGrade = await AddonModQuiz.getUserBestGrade(quiz.id, { cmId: this.module.id });
|
||||
|
||||
try {
|
||||
// Get gradebook grade.
|
||||
const data = await AddonModQuiz.getGradeFromGradebook(this.courseId!, this.module!.id);
|
||||
const data = await AddonModQuiz.getGradeFromGradebook(this.courseId, this.module.id);
|
||||
|
||||
if (data) {
|
||||
this.gradebookData = {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
<core-course-module-description *ngIf="mode != 'iframe' && (mode != 'embedded' || displayDescription)"
|
||||
[description]="description" [component]="component" [componentId]="componentId" contextLevel="module"
|
||||
[contextInstanceId]="module!.id" [courseId]="courseId">
|
||||
[contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-course-module-description>
|
||||
|
||||
<ion-card class="core-warning-card" *ngIf="warning">
|
||||
|
|
|
@ -64,8 +64,8 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
|
|||
|
||||
await this.loadContent();
|
||||
try {
|
||||
await AddonModResource.logView(this.module!.instance!, this.module!.name);
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
await AddonModResource.logView(this.module.instance!, this.module.name);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
} catch {
|
||||
// Ignore errors.
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
|
|||
* @return Resolved when done.
|
||||
*/
|
||||
protected async invalidateContent(): Promise<void> {
|
||||
return AddonModResource.invalidateContent(this.module!.id, this.courseId!);
|
||||
return AddonModResource.invalidateContent(this.module.id, this.courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,9 +88,9 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
|
|||
*/
|
||||
protected async fetchContent(refresh?: boolean): Promise<void> {
|
||||
// Load module contents if needed. Passing refresh is needed to force reloading contents.
|
||||
await CoreCourse.loadModuleContents(this.module!, this.courseId, undefined, false, refresh);
|
||||
await CoreCourse.loadModuleContents(this.module, this.courseId, undefined, false, refresh);
|
||||
|
||||
if (!this.module!.contents || !this.module!.contents.length) {
|
||||
if (!this.module.contents || !this.module.contents.length) {
|
||||
throw new CoreError(Translate.instant('core.filenotfound'));
|
||||
}
|
||||
|
||||
|
@ -99,11 +99,11 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
|
|||
|
||||
// Get the resource instance to get the latest name/description and to know if it's embedded.
|
||||
if (this.canGetResource) {
|
||||
resource = await CoreUtils.ignoreErrors(AddonModResource.getResourceData(this.courseId!, this.module!.id));
|
||||
resource = await CoreUtils.ignoreErrors(AddonModResource.getResourceData(this.courseId, this.module.id));
|
||||
this.description = resource?.intro || '';
|
||||
options = resource?.displayoptions ? CoreTextUtils.unserialize(resource.displayoptions) : {};
|
||||
} else {
|
||||
resource = await CoreUtils.ignoreErrors(CoreCourse.getModule(this.module!.id, this.courseId));
|
||||
resource = await CoreUtils.ignoreErrors(CoreCourse.getModule(this.module.id, this.courseId));
|
||||
this.description = resource?.description || '';
|
||||
options = resource?.customdata ? CoreTextUtils.unserialize(CoreTextUtils.parseJSON(resource.customdata)) : {};
|
||||
}
|
||||
|
@ -114,9 +114,9 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
|
|||
this.dataRetrieved.emit(resource);
|
||||
}
|
||||
|
||||
if (AddonModResourceHelper.isDisplayedInIframe(this.module!)) {
|
||||
if (AddonModResourceHelper.isDisplayedInIframe(this.module)) {
|
||||
const downloadResult = await this.downloadResourceIfNeeded(refresh, true);
|
||||
const src = await AddonModResourceHelper.getIframeSrc(this.module!);
|
||||
const src = await AddonModResourceHelper.getIframeSrc(this.module);
|
||||
this.mode = 'iframe';
|
||||
|
||||
if (this.src && src.toString() == this.src.toString()) {
|
||||
|
@ -137,11 +137,11 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
|
|||
return;
|
||||
}
|
||||
|
||||
if (resource && 'display' in resource && AddonModResourceHelper.isDisplayedEmbedded(this.module!, resource.display)) {
|
||||
if (resource && 'display' in resource && AddonModResourceHelper.isDisplayedEmbedded(this.module, resource.display)) {
|
||||
this.mode = 'embedded';
|
||||
this.warning = '';
|
||||
|
||||
this.contentText = await AddonModResourceHelper.getEmbeddedHtml(this.module!, this.courseId!);
|
||||
this.contentText = await AddonModResourceHelper.getEmbeddedHtml(this.module, this.courseId);
|
||||
this.mode = this.contentText.length > 0 ? 'embedded' : 'external';
|
||||
} else {
|
||||
this.mode = 'external';
|
||||
|
@ -158,20 +158,20 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
async open(): Promise<void> {
|
||||
let downloadable = await CoreCourseModulePrefetchDelegate.isModuleDownloadable(this.module!, this.courseId!);
|
||||
let downloadable = await CoreCourseModulePrefetchDelegate.isModuleDownloadable(this.module, this.courseId);
|
||||
|
||||
if (downloadable) {
|
||||
// Check if the main file is downloadle.
|
||||
// This isn't done in "isDownloadable" to prevent extra WS calls in the course page.
|
||||
downloadable = await AddonModResourceHelper.isMainFileDownloadable(this.module!);
|
||||
downloadable = await AddonModResourceHelper.isMainFileDownloadable(this.module);
|
||||
|
||||
if (downloadable) {
|
||||
return AddonModResourceHelper.openModuleFile(this.module!, this.courseId!);
|
||||
return AddonModResourceHelper.openModuleFile(this.module, this.courseId);
|
||||
}
|
||||
}
|
||||
|
||||
// The resource cannot be downloaded, open the activity in browser.
|
||||
await CoreSites.getCurrentSite()?.openInBrowserWithAutoLoginIfSameSite(this.module!.url!);
|
||||
await CoreSites.getCurrentSite()?.openInBrowserWithAutoLoginIfSameSite(this.module.url!);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<ion-content>
|
||||
<ion-refresher slot="fixed"
|
||||
[disabled]="!activityComponent?.loaded || activityComponent?.mode != 'external'"
|
||||
(ionRefresh)="activityComponent?.doRefresh($event)">
|
||||
(ionRefresh)="activityComponent?.doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||
|
||||
<core-course-module-description *ngIf="displayDescription" [description]="description" [component]="component"
|
||||
[componentId]="componentId" contextLevel="module" [contextInstanceId]="module!.id" [courseId]="courseId">
|
||||
[componentId]="componentId" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-course-module-description>
|
||||
|
||||
<div *ngIf="shouldIframe || (shouldEmbed && isOther)" class="addon-mod_url-embedded-url">
|
||||
|
|
|
@ -75,7 +75,7 @@ export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceCompo
|
|||
* @return Resolved when done.
|
||||
*/
|
||||
protected async invalidateContent(): Promise<void> {
|
||||
await AddonModUrl.invalidateContent(this.module!.id, this.courseId!);
|
||||
await AddonModUrl.invalidateContent(this.module.id, this.courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,7 +90,7 @@ export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceCompo
|
|||
throw null;
|
||||
}
|
||||
// Fetch the module data.
|
||||
const url = await AddonModUrl.getUrl(this.courseId!, this.module!.id);
|
||||
const url = await AddonModUrl.getUrl(this.courseId, this.module.id);
|
||||
|
||||
this.name = url.name;
|
||||
this.description = url.intro;
|
||||
|
@ -102,17 +102,17 @@ export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceCompo
|
|||
}
|
||||
|
||||
// Try to load module contents, it's needed to get the URL with parameters.
|
||||
await CoreCourse.loadModuleContents(this.module!, this.courseId, undefined, false, refresh, undefined, 'url');
|
||||
await CoreCourse.loadModuleContents(this.module, this.courseId, undefined, false, refresh, undefined, 'url');
|
||||
|
||||
// Always use the URL from the module because it already includes the parameters.
|
||||
this.url = this.module!.contents[0] && this.module!.contents[0].fileurl ? this.module!.contents[0].fileurl : undefined;
|
||||
this.url = this.module.contents[0] && this.module.contents[0].fileurl ? this.module.contents[0].fileurl : undefined;
|
||||
|
||||
await this.calculateDisplayOptions(url);
|
||||
|
||||
} catch {
|
||||
// Fallback in case is not prefetched or not available.
|
||||
const mod =
|
||||
await CoreCourse.getModule(this.module!.id, this.courseId, undefined, false, false, undefined, 'url');
|
||||
await CoreCourse.getModule(this.module.id, this.courseId, undefined, false, false, undefined, 'url');
|
||||
|
||||
this.name = mod.name;
|
||||
this.description = mod.description;
|
||||
|
@ -167,8 +167,8 @@ export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceCompo
|
|||
*/
|
||||
protected async logView(): Promise<void> {
|
||||
try {
|
||||
await AddonModUrl.logView(this.module!.instance!, this.module!.name);
|
||||
CoreCourse.checkModuleCompletion(this.courseId!, this.module!.completiondata);
|
||||
await AddonModUrl.logView(this.module.instance!, this.module.name);
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
} catch {
|
||||
// Ignore errors.
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!activityComponent?.loaded" (ionRefresh)="activityComponent?.doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!notificationsLoaded" (ionRefresh)="refreshNotifications($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!notificationsLoaded" (ionRefresh)="refreshNotifications($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="notificationsLoaded">
|
||||
|
|
|
@ -174,13 +174,13 @@ export class AddonNotificationsListPage implements OnInit, OnDestroy {
|
|||
* @param refresher Refresher.
|
||||
* @return Promise<any> Promise resolved when done.
|
||||
*/
|
||||
async refreshNotifications(refresher?: CustomEvent<IonRefresher>): Promise<void> {
|
||||
async refreshNotifications(refresher?: IonRefresher): Promise<void> {
|
||||
await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationsList());
|
||||
|
||||
try {
|
||||
await this.fetchNotifications(true);
|
||||
} finally {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!preferencesLoaded || !notifPrefsEnabled" (ionRefresh)="refreshPreferences($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!preferencesLoaded || !notifPrefsEnabled"
|
||||
(ionRefresh)="refreshPreferences($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="preferencesLoaded">
|
||||
|
|
|
@ -179,13 +179,13 @@ export class AddonNotificationsSettingsPage implements OnInit, OnDestroy {
|
|||
*
|
||||
* @param refresher Refresher.
|
||||
*/
|
||||
async refreshPreferences(refresher?: CustomEvent<IonRefresher>): Promise<void> {
|
||||
async refreshPreferences(refresher?: IonRefresher): Promise<void> {
|
||||
try {
|
||||
await CoreUtils.ignoreErrors(AddonNotifications.invalidateNotificationPreferences());
|
||||
|
||||
await this.fetchPreferences();
|
||||
} finally {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!filesLoaded || (!showPrivateFiles && !showSiteFiles)"
|
||||
(ionRefresh)="refreshData($event)">
|
||||
(ionRefresh)="refreshData($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
|
|
@ -119,9 +119,9 @@ export class AddonPrivateFilesIndexPage implements OnInit, OnDestroy {
|
|||
*
|
||||
* @param refresher Refresher.
|
||||
*/
|
||||
refreshData(event?: CustomEvent<IonRefresher>): void {
|
||||
refreshData(event?: IonRefresher): void {
|
||||
this.refreshFiles().finally(() => {
|
||||
event?.detail.complete();
|
||||
event?.complete();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -71,10 +71,10 @@ export abstract class CoreBlockBaseComponent implements OnInit {
|
|||
* @param showErrors If show errors to the user of hide them.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async doRefresh(refresher?: CustomEvent<IonRefresher>, done?: () => void, showErrors: boolean = false): Promise<void> {
|
||||
async doRefresh(refresher?: IonRefresher, done?: () => void, showErrors: boolean = false): Promise<void> {
|
||||
if (this.loaded) {
|
||||
return this.refreshContent(showErrors).finally(() => {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
done && done();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ export class CoreBlockComponent implements OnInit, OnDestroy, DoCheck {
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
async doRefresh(
|
||||
refresher?: CustomEvent<IonRefresher>,
|
||||
refresher?: IonRefresher,
|
||||
done?: () => void,
|
||||
showErrors: boolean = false,
|
||||
): Promise<void> {
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!commentsLoaded" (ionRefresh)="refreshComments(false, $event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!commentsLoaded" (ionRefresh)="refreshComments(false, $event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="commentsLoaded">
|
||||
|
|
|
@ -205,7 +205,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy {
|
|||
* @param refresher Refresher.
|
||||
* @return Resolved when done.
|
||||
*/
|
||||
async refreshComments(showErrors: boolean, refresher?: CustomEvent<IonRefresher>): Promise<void> {
|
||||
async refreshComments(showErrors: boolean, refresher?: IonRefresher): Promise<void> {
|
||||
this.commentsLoaded = false;
|
||||
this.refreshIcon = CoreConstants.ICON_LOADING;
|
||||
this.syncIcon = CoreConstants.ICON_LOADING;
|
||||
|
@ -219,7 +219,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy {
|
|||
try {
|
||||
await this.fetchComments(true, showErrors);
|
||||
} finally {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,7 +132,7 @@ import { ADDON_MOD_FORUM_SERVICES } from '@addons/mod/forum/forum.module';
|
|||
// @todo import { ADDON_MOD_H5P_ACTIVITY_SERVICES } from '@addons/mod/h5pactivity/h5pactivity.module';
|
||||
import { ADDON_MOD_IMSCP_SERVICES } from '@addons/mod/imscp/imscp.module';
|
||||
import { ADDON_MOD_LESSON_SERVICES } from '@addons/mod/lesson/lesson.module';
|
||||
// @todo import { ADDON_MOD_LTI_SERVICES } from '@addons/mod/lti/lti.module';
|
||||
import { ADDON_MOD_LTI_SERVICES } from '@addons/mod/lti/lti.module';
|
||||
import { ADDON_MOD_PAGE_SERVICES } from '@addons/mod/page/page.module';
|
||||
import { ADDON_MOD_QUIZ_SERVICES } from '@addons/mod/quiz/quiz.module';
|
||||
import { ADDON_MOD_RESOURCE_SERVICES } from '@addons/mod/resource/resource.module';
|
||||
|
@ -297,7 +297,7 @@ export class CoreCompileProvider {
|
|||
// @todo ...ADDON_MOD_H5P_ACTIVITY_SERVICES,
|
||||
...ADDON_MOD_IMSCP_SERVICES,
|
||||
...ADDON_MOD_LESSON_SERVICES,
|
||||
// @todo ...ADDON_MOD_LTI_SERVICES,
|
||||
...ADDON_MOD_LTI_SERVICES,
|
||||
...ADDON_MOD_PAGE_SERVICES,
|
||||
...ADDON_MOD_QUIZ_SERVICES,
|
||||
...ADDON_MOD_RESOURCE_SERVICES,
|
||||
|
|
|
@ -50,8 +50,8 @@ export type CoreCourseResourceDownloadResult = {
|
|||
})
|
||||
export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy, CoreCourseModuleMainComponent {
|
||||
|
||||
@Input() module?: CoreCourseModule; // The module of the component.
|
||||
@Input() courseId?: number; // Course ID the component belongs to.
|
||||
@Input() module!: CoreCourseModule; // The module of the component.
|
||||
@Input() courseId!: number; // Course ID the component belongs to.
|
||||
@Output() dataRetrieved = new EventEmitter<unknown>(); // Called to notify changes the index page from the main component.
|
||||
|
||||
loaded = false; // If the component has been loaded.
|
||||
|
@ -90,10 +90,10 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
|
|||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.siteId = CoreSites.getCurrentSiteId();
|
||||
this.description = this.module?.description;
|
||||
this.componentId = this.module?.id;
|
||||
this.externalUrl = this.module?.url;
|
||||
this.courseId = this.courseId || this.module?.course;
|
||||
this.description = this.module.description;
|
||||
this.componentId = this.module.id;
|
||||
this.externalUrl = this.module.url;
|
||||
this.courseId = this.courseId || this.module.course!;
|
||||
this.blog = await AddonBlog.isPluginEnabled();
|
||||
}
|
||||
|
||||
|
@ -105,8 +105,9 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
|
|||
* @param showErrors If show errors to the user of hide them.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async doRefresh(refresher?: CustomEvent<IonRefresher> | null, done?: () => void, showErrors: boolean = false): Promise<void> {
|
||||
async doRefresh(refresher?: IonRefresher | null, done?: () => void, showErrors: boolean = false): Promise<void> {
|
||||
if (!this.loaded || !this.module) {
|
||||
// Module can be undefined if course format changes from single activity to weekly/topics.
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -118,7 +119,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
|
|||
|
||||
await CoreUtils.ignoreErrors(this.refreshContent(true, showErrors));
|
||||
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
done && done();
|
||||
}
|
||||
|
||||
|
@ -193,12 +194,8 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
|
|||
* Fill the context menu options
|
||||
*/
|
||||
protected fillContextMenu(refresh: boolean = false): void {
|
||||
if (!this.module) {
|
||||
return;
|
||||
}
|
||||
|
||||
// All data obtained, now fill the context menu.
|
||||
CoreCourseHelper.fillContextMenu(this, this.module, this.courseId!, refresh, this.component);
|
||||
CoreCourseHelper.fillContextMenu(this, this.module, this.courseId, refresh, this.component);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -215,10 +212,10 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
|
|||
expandDescription(): void {
|
||||
CoreTextUtils.viewText(Translate.instant('core.description'), this.description!, {
|
||||
component: this.component,
|
||||
componentId: this.module?.id,
|
||||
componentId: this.module.id,
|
||||
filter: true,
|
||||
contextLevel: 'module',
|
||||
instanceId: this.module?.id,
|
||||
instanceId: this.module.id,
|
||||
courseId: this.courseId,
|
||||
});
|
||||
}
|
||||
|
@ -227,7 +224,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
|
|||
* Go to blog posts.
|
||||
*/
|
||||
async gotoBlog(): Promise<void> {
|
||||
const params: Params = { cmId: this.module?.id };
|
||||
const params: Params = { cmId: this.module.id };
|
||||
|
||||
CoreNavigator.navigateToSitePath(AddonBlogMainMenuHandlerService.PAGE_NAME, { params });
|
||||
}
|
||||
|
@ -238,11 +235,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
|
|||
* @param done Function to call when done.
|
||||
*/
|
||||
prefetch(done?: () => void): void {
|
||||
if (!this.module) {
|
||||
return;
|
||||
}
|
||||
|
||||
CoreCourseHelper.contextMenuPrefetch(this, this.module, this.courseId!, done);
|
||||
CoreCourseHelper.contextMenuPrefetch(this, this.module, this.courseId, done);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -251,17 +244,13 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
|
|||
* @param done Function to call when done.
|
||||
*/
|
||||
removeFiles(done?: () => void): void {
|
||||
if (!this.module) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.prefetchStatus == CoreConstants.DOWNLOADING) {
|
||||
CoreDomUtils.showAlertTranslated(undefined, 'core.course.cannotdeletewhiledownloading');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CoreCourseHelper.confirmAndRemoveFiles(this.module, this.courseId!, done);
|
||||
CoreCourseHelper.confirmAndRemoveFiles(this.module, this.courseId, done);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -309,13 +298,13 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async setStatusListener(): Promise<void> {
|
||||
if (typeof this.statusObserver != 'undefined' || !this.module) {
|
||||
if (typeof this.statusObserver != 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Listen for changes on this module status.
|
||||
this.statusObserver = CoreEvents.on(CoreEvents.PACKAGE_STATUS_CHANGED, (data) => {
|
||||
if (!this.module || data.componentId != this.module.id || data.component != this.component) {
|
||||
if (data.componentId != this.module.id || data.component != this.component) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -327,7 +316,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
|
|||
}, this.siteId);
|
||||
|
||||
// Also, get the current status.
|
||||
const status = await CoreCourseModulePrefetchDelegate.getModuleStatus(this.module, this.courseId!);
|
||||
const status = await CoreCourseModulePrefetchDelegate.getModuleStatus(this.module, this.courseId);
|
||||
|
||||
this.currentStatus = status;
|
||||
this.showStatus(status);
|
||||
|
@ -350,17 +339,13 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
|
|||
failed: false,
|
||||
};
|
||||
|
||||
if (!this.module) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get module status to determine if it needs to be downloaded.
|
||||
await this.setStatusListener();
|
||||
|
||||
if (this.currentStatus != CoreConstants.DOWNLOADED) {
|
||||
// Download content. This function also loads module contents if needed.
|
||||
try {
|
||||
await CoreCourseModulePrefetchDelegate.downloadModule(this.module, this.courseId!);
|
||||
await CoreCourseModulePrefetchDelegate.downloadModule(this.module, this.courseId);
|
||||
|
||||
// If we reach here it means the download process already loaded the contents, no need to do it again.
|
||||
contentsAlreadyLoaded = true;
|
||||
|
|
|
@ -497,7 +497,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
|||
* @param afterCompletionChange Whether the refresh is due to a completion change.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async doRefresh(refresher?: CustomEvent<IonRefresher>, done?: () => void, afterCompletionChange?: boolean): Promise<void> {
|
||||
async doRefresh(refresher?: IonRefresher, done?: () => void, afterCompletionChange?: boolean): Promise<void> {
|
||||
const promises = this.dynamicComponents?.map(async (component) => {
|
||||
await component.callComponentFunction('doRefresh', [refresher, done, afterCompletionChange]);
|
||||
}) || [];
|
||||
|
@ -508,7 +508,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
|||
|
||||
await Promise.all(promises);
|
||||
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
done?.();
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges {
|
|||
* @param afterCompletionChange Whether the refresh is due to a completion change.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async doRefresh(refresher?: CustomEvent<IonRefresher>, done?: () => void, afterCompletionChange?: boolean): Promise<void> {
|
||||
async doRefresh(refresher?: IonRefresher, done?: () => void, afterCompletionChange?: boolean): Promise<void> {
|
||||
if (afterCompletionChange) {
|
||||
// Don't refresh the view after a completion change since completion isn't displayed.
|
||||
return;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!dataLoaded || !displayRefresher" (ionRefresh)="doRefresh($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!dataLoaded || !displayRefresher" (ionRefresh)="doRefresh($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
|
@ -26,4 +26,4 @@
|
|||
(completionChanged)="onCompletionChange($event)" class="core-course-format-{{course.format}}">
|
||||
</core-course-format>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
</ion-content>
|
||||
|
|
|
@ -327,7 +327,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
|
|||
* @param refresher Refresher.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async doRefresh(refresher?: CustomEvent<IonRefresher>): Promise<void> {
|
||||
async doRefresh(refresher?: IonRefresher): Promise<void> {
|
||||
await CoreUtils.ignoreErrors(this.invalidateData());
|
||||
|
||||
try {
|
||||
|
@ -339,7 +339,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
|
|||
await CoreUtils.ignoreErrors(this.formatComponent.doRefresh(refresher));
|
||||
}
|
||||
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refreshData($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="loaded">
|
||||
|
@ -25,4 +25,4 @@
|
|||
</ng-container>
|
||||
</ion-list>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
</ion-content>
|
||||
|
|
|
@ -119,13 +119,13 @@ export class CoreCourseListModTypePage implements OnInit {
|
|||
* @param refresher Refresher.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async refreshData(refresher: CustomEvent<IonRefresher>): Promise<void> {
|
||||
async refreshData(refresher: IonRefresher): Promise<void> {
|
||||
await CoreUtils.ignoreErrors(CoreCourse.invalidateSections(this.courseId || 0));
|
||||
|
||||
try {
|
||||
await this.fetchData();
|
||||
} finally {
|
||||
refresher.detail.complete();
|
||||
refresher.complete();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-refresher slot="fixed" [disabled]="!dataLoaded" (ionRefresh)="refreshData($event)">
|
||||
<ion-refresher slot="fixed" [disabled]="!dataLoaded" (ionRefresh)="refreshData($event.target)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="dataLoaded">
|
||||
|
|
|
@ -379,7 +379,7 @@ export class CoreCoursePreviewPage implements OnInit, OnDestroy {
|
|||
*
|
||||
* @param refresher The refresher if this was triggered by a Pull To Refresh.
|
||||
*/
|
||||
async refreshData(refresher?: CustomEvent<IonRefresher>): Promise<void> {
|
||||
async refreshData(refresher?: IonRefresher): Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
promises.push(CoreCourses.invalidateUserCourses());
|
||||
|
@ -394,7 +394,7 @@ export class CoreCoursePreviewPage implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
await Promise.all(promises).finally(() => this.getCourse()).finally(() => {
|
||||
refresher?.detail.complete();
|
||||
refresher?.complete();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -184,7 +184,7 @@ export interface CoreCourseModuleMainComponent {
|
|||
* @param done Function to call when done.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
doRefresh(refresher?: CustomEvent<IonRefresher>, done?: () => void): Promise<void>;
|
||||
doRefresh(refresher?: IonRefresher, done?: () => void): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue