commit
9ecf908d24
|
@ -124,7 +124,7 @@
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin name="cordova-plugin-device" spec="2.0.2" />
|
<plugin name="cordova-plugin-device" spec="2.0.2" />
|
||||||
<plugin name="cordova-plugin-file" spec="6.0.1" />
|
<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-file-transfer" spec="1.7.1" />
|
||||||
<plugin name="cordova-plugin-globalization" spec="1.11.0" />
|
<plugin name="cordova-plugin-globalization" spec="1.11.0" />
|
||||||
<plugin name="cordova-plugin-inappbrowser" spec="3.0.0" />
|
<plugin name="cordova-plugin-inappbrowser" spec="3.0.0" />
|
||||||
|
|
|
@ -145,6 +145,7 @@
|
||||||
"addon.coursecompletion.criteriarequiredany": "completion",
|
"addon.coursecompletion.criteriarequiredany": "completion",
|
||||||
"addon.coursecompletion.inprogress": "completion",
|
"addon.coursecompletion.inprogress": "completion",
|
||||||
"addon.coursecompletion.manualselfcompletion": "completion",
|
"addon.coursecompletion.manualselfcompletion": "completion",
|
||||||
|
"addon.coursecompletion.nottracked": "completion",
|
||||||
"addon.coursecompletion.notyetstarted": "completion",
|
"addon.coursecompletion.notyetstarted": "completion",
|
||||||
"addon.coursecompletion.pending": "completion",
|
"addon.coursecompletion.pending": "completion",
|
||||||
"addon.coursecompletion.required": "moodle",
|
"addon.coursecompletion.required": "moodle",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
</ion-refresher>
|
</ion-refresher>
|
||||||
<core-loading [hideUntil]="completionLoaded">
|
<core-loading [hideUntil]="completionLoaded">
|
||||||
<ion-card *ngIf="completion">
|
<ion-card *ngIf="completion && tracked">
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<h2>{{ 'addon.coursecompletion.status' | translate }}</h2>
|
<h2>{{ 'addon.coursecompletion.status' | translate }}</h2>
|
||||||
<p>{{ completion.statusText | translate }}</p>
|
<p>{{ completion.statusText | translate }}</p>
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
<p *ngIf="completion.aggregation === 2">{{ 'addon.coursecompletion.criteriarequiredany' | translate }}</p>
|
<p *ngIf="completion.aggregation === 2">{{ 'addon.coursecompletion.criteriarequiredany' | translate }}</p>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
<ion-card *ngIf="completion">
|
<ion-card *ngIf="completion && tracked">
|
||||||
<ion-item-divider>{{ 'addon.coursecompletion.requiredcriteria' | translate }}</ion-item-divider>
|
<ion-item-divider>{{ 'addon.coursecompletion.requiredcriteria' | translate }}</ion-item-divider>
|
||||||
<ion-item class="hidden-tablet" text-wrap *ngFor="let criteria of completion.completions">
|
<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>
|
<h2><core-format-text clean="true" [text]="criteria.details.criteria"></core-format-text></h2>
|
||||||
|
@ -41,11 +41,16 @@
|
||||||
</ion-row>
|
</ion-row>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
<ion-card *ngIf="showSelfComplete">
|
<ion-card *ngIf="showSelfComplete && tracked">
|
||||||
<ion-item-divider>{{ 'addon.coursecompletion.manualselfcompletion' | translate }}</ion-item-divider>
|
<ion-item-divider>{{ 'addon.coursecompletion.manualselfcompletion' | translate }}</ion-item-divider>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<button ion-button block (click)="completeCourse()">{{ 'addon.coursecompletion.completecourse' | translate }}</button>
|
<button ion-button block (click)="completeCourse()">{{ 'addon.coursecompletion.completecourse' | translate }}</button>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
|
<div *ngIf="!tracked" class="core-warning-card" icon-start>
|
||||||
|
<ion-icon name="warning"></ion-icon>
|
||||||
|
{{ 'addon.coursecompletion.nottracked' | translate }}
|
||||||
|
</div>
|
||||||
</core-loading>
|
</core-loading>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
|
@ -31,6 +31,7 @@ export class AddonCourseCompletionReportComponent implements OnInit {
|
||||||
completionLoaded = false;
|
completionLoaded = false;
|
||||||
completion: any;
|
completion: any;
|
||||||
showSelfComplete: boolean;
|
showSelfComplete: boolean;
|
||||||
|
tracked = true; // Whether completion is tracked.
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private sitesProvider: CoreSitesProvider,
|
private sitesProvider: CoreSitesProvider,
|
||||||
|
@ -62,8 +63,14 @@ export class AddonCourseCompletionReportComponent implements OnInit {
|
||||||
|
|
||||||
this.completion = completion;
|
this.completion = completion;
|
||||||
this.showSelfComplete = this.courseCompletionProvider.canMarkSelfCompleted(this.userId, completion);
|
this.showSelfComplete = this.courseCompletionProvider.canMarkSelfCompleted(this.userId, completion);
|
||||||
}).catch((message) => {
|
this.tracked = true;
|
||||||
this.domUtils.showErrorModalDefault(message, 'addon.coursecompletion.couldnotloadreport', 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);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"criteriarequiredany": "Any criteria below are required",
|
"criteriarequiredany": "Any criteria below are required",
|
||||||
"inprogress": "In progress",
|
"inprogress": "In progress",
|
||||||
"manualselfcompletion": "Manual self completion",
|
"manualselfcompletion": "Manual self completion",
|
||||||
|
"nottracked": "You are currently not being tracked by completion in this course",
|
||||||
"notyetstarted": "Not yet started",
|
"notyetstarted": "Not yet started",
|
||||||
"pending": "Pending",
|
"pending": "Pending",
|
||||||
"required": "Required",
|
"required": "Required",
|
||||||
|
|
|
@ -145,6 +145,7 @@
|
||||||
"addon.coursecompletion.criteriarequiredany": "Any criteria below are required",
|
"addon.coursecompletion.criteriarequiredany": "Any criteria below are required",
|
||||||
"addon.coursecompletion.inprogress": "In progress",
|
"addon.coursecompletion.inprogress": "In progress",
|
||||||
"addon.coursecompletion.manualselfcompletion": "Manual self completion",
|
"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.notyetstarted": "Not yet started",
|
||||||
"addon.coursecompletion.pending": "Pending",
|
"addon.coursecompletion.pending": "Pending",
|
||||||
"addon.coursecompletion.required": "Required",
|
"addon.coursecompletion.required": "Required",
|
||||||
|
|
|
@ -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.
|
// Do not pass those options to the core WS factory.
|
||||||
return this.wsProvider.call(method, data, wsPreSets).then((response) => {
|
return this.wsProvider.call(method, data, wsPreSets).then((response) => {
|
||||||
if (preSets.saveToCache) {
|
if (preSets.saveToCache) {
|
||||||
|
@ -688,8 +688,9 @@ export class CoreSite {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.ongoingRequests[cacheId] = promise;
|
this.ongoingRequests[cacheId] = promise;
|
||||||
|
|
||||||
// Clear ongoing request after setting the promise (just in case it's already resolved).
|
// Clear ongoing request after setting the promise (just in case it's already resolved).
|
||||||
promise.finally(() => {
|
promise = promise.finally(() => {
|
||||||
// Make sure we don't clear the promise of a newer request that ignores the cache.
|
// Make sure we don't clear the promise of a newer request that ignores the cache.
|
||||||
if (this.ongoingRequests[cacheId] === promise) {
|
if (this.ongoingRequests[cacheId] === promise) {
|
||||||
delete this.ongoingRequests[cacheId];
|
delete this.ongoingRequests[cacheId];
|
||||||
|
|
|
@ -169,7 +169,9 @@ export class CoreCourseSectionPage implements OnDestroy {
|
||||||
*/
|
*/
|
||||||
protected loadData(refresh?: boolean, sync?: boolean): Promise<any> {
|
protected loadData(refresh?: boolean, sync?: boolean): Promise<any> {
|
||||||
// First of all, get the course because the data might have changed.
|
// 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.
|
// Error getting the course, probably guest access.
|
||||||
}).then((course) => {
|
}).then((course) => {
|
||||||
if (course) {
|
if (course) {
|
||||||
|
|
|
@ -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.
|
* Check if the course has a block with that name.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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.
|
* Get courses. They can be filtered by field.
|
||||||
*
|
*
|
||||||
|
@ -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.
|
* @return {boolean} Whether get courses by field is available.
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
*/
|
*/
|
||||||
isGetCoursesByFieldAvailable(): boolean {
|
isGetCoursesByFieldAvailable(site?: CoreSite): boolean {
|
||||||
return this.sitesProvider.wsAvailableInCurrentSite('core_course_get_courses_by_field');
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue