Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
</plugin>
<plugin name="cordova-plugin-device" spec="2.0.2" />
<plugin name="cordova-plugin-file" spec="6.0.1" />
<plugin name="cordova-plugin-file-opener2" spec="2.2.0" />
<plugin name="cordova-plugin-file-opener2" spec="2.0.19" />
<plugin name="cordova-plugin-file-transfer" spec="1.7.1" />
<plugin name="cordova-plugin-globalization" spec="1.11.0" />
<plugin name="cordova-plugin-inappbrowser" spec="3.0.0" />
Expand Down
1 change: 1 addition & 0 deletions scripts/langindex.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
"addon.coursecompletion.criteriarequiredany": "completion",
"addon.coursecompletion.inprogress": "completion",
"addon.coursecompletion.manualselfcompletion": "completion",
"addon.coursecompletion.nottracked": "completion",
"addon.coursecompletion.notyetstarted": "completion",
"addon.coursecompletion.pending": "completion",
"addon.coursecompletion.required": "moodle",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher>
<core-loading [hideUntil]="completionLoaded">
<ion-card *ngIf="completion">
<ion-card *ngIf="completion && tracked">
<ion-item text-wrap>
<h2>{{ 'addon.coursecompletion.status' | translate }}</h2>
<p>{{ completion.statusText | translate }}</p>
Expand All @@ -14,7 +14,7 @@ <h2>{{ 'addon.coursecompletion.required' | translate }}</h2>
<p *ngIf="completion.aggregation === 2">{{ 'addon.coursecompletion.criteriarequiredany' | translate }}</p>
</ion-item>
</ion-card>
<ion-card *ngIf="completion">
<ion-card *ngIf="completion && tracked">
<ion-item-divider>{{ 'addon.coursecompletion.requiredcriteria' | translate }}</ion-item-divider>
<ion-item class="hidden-tablet" text-wrap *ngFor="let criteria of completion.completions">
<h2><core-format-text clean="true" [text]="criteria.details.criteria"></core-format-text></h2>
Expand All @@ -41,11 +41,16 @@ <h2><core-format-text clean="true" [text]="criteria.details.criteria"></core-for
</ion-row>
</ion-item>
</ion-card>
<ion-card *ngIf="showSelfComplete">
<ion-card *ngIf="showSelfComplete && tracked">
<ion-item-divider>{{ 'addon.coursecompletion.manualselfcompletion' | translate }}</ion-item-divider>
<ion-item>
<button ion-button block (click)="completeCourse()">{{ 'addon.coursecompletion.completecourse' | translate }}</button>
</ion-item>
</ion-card>

<div *ngIf="!tracked" class="core-warning-card" icon-start>
<ion-icon name="warning"></ion-icon>
{{ 'addon.coursecompletion.nottracked' | translate }}
</div>
</core-loading>
</ion-content>
11 changes: 9 additions & 2 deletions src/addon/coursecompletion/components/report/report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export class AddonCourseCompletionReportComponent implements OnInit {
completionLoaded = false;
completion: any;
showSelfComplete: boolean;
tracked = true; // Whether completion is tracked.

constructor(
private sitesProvider: CoreSitesProvider,
Expand Down Expand Up @@ -62,8 +63,14 @@ export class AddonCourseCompletionReportComponent implements OnInit {

this.completion = completion;
this.showSelfComplete = this.courseCompletionProvider.canMarkSelfCompleted(this.userId, completion);
}).catch((message) => {
this.domUtils.showErrorModalDefault(message, 'addon.coursecompletion.couldnotloadreport', true);
this.tracked = true;
}).catch((error) => {
if (error && error.errorcode == 'notenroled') {
// Not enrolled error, probably a teacher.
this.tracked = false;
} else {
this.domUtils.showErrorModalDefault(error, 'addon.coursecompletion.couldnotloadreport', true);
}
});
}

Expand Down
1 change: 1 addition & 0 deletions src/addon/coursecompletion/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"criteriarequiredany": "Any criteria below are required",
"inprogress": "In progress",
"manualselfcompletion": "Manual self completion",
"nottracked": "You are currently not being tracked by completion in this course",
"notyetstarted": "Not yet started",
"pending": "Pending",
"required": "Required",
Expand Down
1 change: 1 addition & 0 deletions src/assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
"addon.coursecompletion.criteriarequiredany": "Any criteria below are required",
"addon.coursecompletion.inprogress": "In progress",
"addon.coursecompletion.manualselfcompletion": "Manual self completion",
"addon.coursecompletion.nottracked": "You are currently not being tracked by completion in this course",
"addon.coursecompletion.notyetstarted": "Not yet started",
"addon.coursecompletion.pending": "Pending",
"addon.coursecompletion.required": "Required",
Expand Down
13 changes: 7 additions & 6 deletions src/classes/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ export class CoreSite {
});
}

const promise = this.getFromCache(method, data, preSets, false, originalData).catch(() => {
let promise = this.getFromCache(method, data, preSets, false, originalData).catch(() => {
// Do not pass those options to the core WS factory.
return this.wsProvider.call(method, data, wsPreSets).then((response) => {
if (preSets.saveToCache) {
Expand Down Expand Up @@ -688,12 +688,13 @@ export class CoreSite {
});

this.ongoingRequests[cacheId] = promise;

// Clear ongoing request after setting the promise (just in case it's already resolved).
promise.finally(() => {
// Make sure we don't clear the promise of a newer request that ignores the cache.
if (this.ongoingRequests[cacheId] === promise) {
delete this.ongoingRequests[cacheId];
}
promise = promise.finally(() => {
// Make sure we don't clear the promise of a newer request that ignores the cache.
if (this.ongoingRequests[cacheId] === promise) {
delete this.ongoingRequests[cacheId];
}
});

return promise.then((response) => {
Expand Down
4 changes: 3 additions & 1 deletion src/core/course/pages/section/section.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ export class CoreCourseSectionPage implements OnDestroy {
*/
protected loadData(refresh?: boolean, sync?: boolean): Promise<any> {
// First of all, get the course because the data might have changed.
return this.coursesProvider.getUserCourse(this.course.id).catch(() => {
return this.courseHelper.getCourse(this.course.id).then((result) => {
return result.course;
}).catch(() => {
// Error getting the course, probably guest access.
}).then((course) => {
if (course) {
Expand Down
27 changes: 27 additions & 0 deletions src/core/course/providers/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,33 @@ export class CoreCourseHelperProvider {
});
}

/**
* Get a course. It will first check the user courses, and fallback to another WS if not enrolled.
*
* @param {number} courseId Course ID.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<{enrolled: boolean, course: any}>} Promise resolved with the course.
*/
getCourse(courseId: number, siteId?: string): Promise<{enrolled: boolean, course: any}> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();

// Try with enrolled courses first.
return this.coursesProvider.getUserCourse(courseId, false, siteId).then((course) => {
return { enrolled: true, course: course };
}).catch(() => {
// Not enrolled or an error happened. Try to use another WebService.
return this.coursesProvider.isGetCoursesByFieldAvailableInSite(siteId).then((available) => {
if (available) {
return this.coursesProvider.getCourseByField('id', courseId, siteId);
} else {
return this.coursesProvider.getCourse(courseId, siteId);
}
}).then((course) => {
return { enrolled: false, course: course };
});
});
}

/**
* Check if the course has a block with that name.
*
Expand Down
46 changes: 43 additions & 3 deletions src/core/courses/providers/courses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,30 @@ export class CoreCoursesProvider {
}
}

/**
* Get the first course returned by getCoursesByField.
*
* @param {string} [field] The field to search. Can be left empty for all courses or:
* id: course id.
* ids: comma separated course ids.
* shortname: course short name.
* idnumber: course id number.
* category: category id the course belongs to.
* @param {any} [value] The value to match.
* @param {string} [siteId] Site ID. If not defined, use current site.
* @return {Promise<any>} Promise resolved with the first course.
* @since 3.2
*/
getCourseByField(field?: string, value?: any, siteId?: string): Promise<any> {
return this.getCoursesByField(field, value, siteId).then((courses) => {
if (courses && courses.length > 0) {
return courses[0];
}

return Promise.reject(null);
});
}

/**
* Get courses. They can be filtered by field.
*
Expand Down Expand Up @@ -482,13 +506,29 @@ export class CoreCoursesProvider {
}

/**
* Check if get courses by field WS is available.
* Check if get courses by field WS is available in a certain site.
*
* @param {CoreSite} [site] Site to check.
* @return {boolean} Whether get courses by field is available.
* @since 3.2
*/
isGetCoursesByFieldAvailable(): boolean {
return this.sitesProvider.wsAvailableInCurrentSite('core_course_get_courses_by_field');
isGetCoursesByFieldAvailable(site?: CoreSite): boolean {
site = site || this.sitesProvider.getCurrentSite();

return site.wsAvailable('core_course_get_courses_by_field');
}

/**
* Check if get courses by field WS is available in a certain site, by site ID.
*
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<boolean>} Promise resolved with boolean: whether get courses by field is available.
* @since 3.2
*/
isGetCoursesByFieldAvailableInSite(siteId?: string): Promise<boolean> {
return this.sitesProvider.getSite(siteId).then((site) => {
return this.isGetCoursesByFieldAvailable(site);
});
}

/**
Expand Down