Merge pull request #1584 from albertgasset/MOBILE-2675

Mobile 2675
main
Juan Leyva 2018-11-06 16:15:27 +01:00 committed by GitHub
commit f651dc983b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 186 additions and 79 deletions

View File

@ -1631,6 +1631,7 @@
"core.view": "moodle",
"core.viewcode": "local_moodlemobileapp",
"core.vieweditor": "local_moodlemobileapp",
"core.viewembeddedcontent": "local_moodlemobileapp",
"core.viewprofile": "moodle",
"core.warningofflinedatadeleted": "local_moodlemobileapp",
"core.whatisyourage": "moodle",

View File

@ -54,9 +54,14 @@ export class AddonCompetencyCompetencyPage {
ionViewDidLoad(): void {
this.fetchCompetency().then(() => {
if (this.planId) {
this.competencyProvider.logCompetencyInPlanView(this.planId, this.competencyId, this.planStatus, this.userId);
this.competencyProvider.logCompetencyInPlanView(this.planId, this.competencyId, this.planStatus, this.userId)
.catch(() => {
// Ignore errors.
});
} else {
this.competencyProvider.logCompetencyInCourseView(this.courseId, this.competencyId, this.userId);
this.competencyProvider.logCompetencyInCourseView(this.courseId, this.competencyId, this.userId).catch(() => {
// Ignore errors.
});
}
}).finally(() => {
this.competencyLoaded = true;

View File

@ -41,7 +41,9 @@ export class AddonCompetencyCompetencySummaryPage {
*/
ionViewDidLoad(): void {
this.fetchCompetency().then(() => {
this.competencyProvider.logCompetencyView(this.competencyId);
this.competencyProvider.logCompetencyView(this.competencyId).catch(() => {
// Ignore errors.
});
}).finally(() => {
this.competencyLoaded = true;
});

View File

@ -230,8 +230,6 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
siteId = siteId || this.sitesProvider.getCurrentSiteId();
promises.push(this.courseProvider.getModuleBasicInfo(module.id, siteId));
// Get assignment to retrieve all its submissions.
promises.push(this.assignProvider.getAssignment(courseId, module.id, siteId).then((assign) => {
const subPromises = [],

View File

@ -168,6 +168,8 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
if (!this.nextChapter) {
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
}
}).catch(() => {
// Ignore errors.
});
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'addon.mod_book.errorchapter', true);

View File

@ -49,6 +49,8 @@ export class AddonModChatIndexComponent extends CoreCourseModuleMainActivityComp
this.loadContent().then(() => {
this.chatProvider.logView(this.chat.id).then(() => {
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
}).catch(() => {
// Ignore errors.
});
});
}

View File

@ -105,6 +105,8 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
this.dataProvider.logView(this.data.id).then(() => {
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
}).catch(() => {
// Ignore errors.
});
});

View File

@ -97,7 +97,9 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
super.ngOnInit();
this.loadContent(false, true).then(() => {
this.feedbackProvider.logView(this.feedback.id);
this.feedbackProvider.logView(this.feedback.id).catch(() => {
// Ignore errors.
});
}).finally(() => {
this.tabsReady = true;
});

View File

@ -96,6 +96,8 @@ export class AddonModFeedbackFormPage implements OnDestroy {
this.fetchData().then(() => {
this.feedbackProvider.logView(this.feedback.id, true).then(() => {
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
}).catch(() => {
// Ignore errors.
});
});
}

View File

@ -57,6 +57,8 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
this.loadContent().then(() => {
this.folderProvider.logView(this.module.instance).then(() => {
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
}).catch(() => {
// Ignore errors.
});
}).finally(() => {
this.loaded = true;

View File

@ -48,7 +48,9 @@ export class AddonModGlossaryEntryPage {
*/
ionViewDidLoad(): void {
this.fetchEntry().then(() => {
this.glossaryProvider.logEntryView(this.entry.id);
this.glossaryProvider.logEntryView(this.entry.id).catch(() => {
// Ignore errors.
});
}).finally(() => {
this.loaded = true;
});

View File

@ -54,6 +54,8 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom
this.loadContent().then(() => {
this.imscpProvider.logView(this.module.instance).then(() => {
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
}).catch(() => {
// Ignore errors.
});
});
}

View File

@ -358,9 +358,6 @@ export class AddonModLessonPrefetchHandler extends CoreCourseActivityPrefetchHan
// Prefetch question attempts in last retake for offline calculations.
promises.push(this.lessonProvider.getQuestionsAttemptsOnline(lesson.id, retake, false, undefined, false, true, siteId));
// Get module info to be able to handle links.
promises.push(this.courseProvider.getModuleBasicInfo(module.id, siteId));
if (accessInfo.canviewreports) {
// Prefetch reports data.
promises.push(this.groupsProvider.getActivityAllowedGroupsIfEnabled(module.id, undefined, siteId).then((groups) => {

View File

@ -55,6 +55,8 @@ export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComp
this.loadContent().then(() => {
this.pageProvider.logView(this.module.instance).then(() => {
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
}).catch(() => {
// Ignore errors.
});
});
}

View File

@ -52,6 +52,8 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
this.loadContent().then(() => {
this.resourceProvider.logView(this.module.instance).then(() => {
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
}).catch(() => {
// Ignore errors.
});
});
}

View File

@ -154,6 +154,8 @@ export class AddonModResourceHelperProvider {
module.contents).then(() => {
this.resourceProvider.logView(module.instance).then(() => {
this.courseProvider.checkModuleCompletion(courseId, module.completionstatus);
}).catch(() => {
// Ignore errors.
});
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'addon.mod_resource.errorwhileloadingthecontent', true);

View File

@ -70,11 +70,6 @@ export class AddonModResourcePrefetchHandler extends CoreCourseResourcePrefetchH
promises.push(this.resourceProvider.getResourceData(courseId, module.id));
}
/* When prefetching we usually use ignoreCache=true. However, this WS call can return a lot of data, so if
a user downloads resources 1 by 1 we would be downloading the same data over and over again. Since
this data won't change often it's probably better to use ignoreCache=false. */
promises.push(this.courseProvider.getModule(module.id, courseId, undefined, false, false, undefined, this.modName));
return Promise.all(promises);
});
}

View File

@ -55,6 +55,8 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo
this.loadContent(false, true).then(() => {
this.surveyProvider.logView(this.survey.id).then(() => {
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
}).catch(() => {
// Ignore errors.
});
});
}

View File

@ -173,6 +173,8 @@ export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceCompo
go(): void {
this.urlProvider.logView(this.module.instance).then(() => {
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
}).catch(() => {
// Ignore errors.
});
this.urlHelper.open(this.url);
}

View File

@ -140,6 +140,9 @@ export class AddonModUrlModuleHandler implements CoreCourseModuleHandler {
return this.courseProvider.loadModuleContents(module, courseId, undefined, false, false, undefined, this.modName)
.then(() => {
return !(module.contents && module.contents[0] && module.contents[0].fileurl);
}).catch(() => {
// Module contents could not be loaded, most probably device is offline.
return true;
});
}
@ -164,6 +167,8 @@ export class AddonModUrlModuleHandler implements CoreCourseModuleHandler {
protected openUrl(module: any, courseId: number): void {
this.urlProvider.logView(module.instance).then(() => {
this.courseProvider.checkModuleCompletion(courseId, module.completionstatus);
}).catch(() => {
// Ignore errors.
});
this.urlHelper.open(module.contents[0].fileurl);
}

View File

@ -341,7 +341,9 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp
this.currentPage = data.pageId;
this.showLoadingAndFetch(true, false).then(() => {
this.wikiProvider.logPageView(this.currentPage);
this.wikiProvider.logPageView(this.currentPage).catch(() => {
// Ignore errors.
});
});
// Stop listening for new page events.

View File

@ -201,7 +201,6 @@ export class AddonModWikiPrefetchHandler extends CoreCourseActivityPrefetchHandl
promises.push(this.wikiProvider.getWiki(courseId, module.id, false, siteId).then((wiki) => {
return this.courseHelper.getModuleCourseIdByInstance(wiki.id, 'wiki', siteId);
}));
promises.push(this.courseProvider.getModuleBasicInfo(module.id, siteId));
// Get related page files and fetch them.
promises.push(this.getFiles(module, courseId, single, siteId).then((files) => {

View File

@ -132,6 +132,8 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy {
this.fetchSubmissionData().then(() => {
this.workshopProvider.logViewSubmission(this.submissionId).then(() => {
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
}).catch(() => {
// Ignore errors.
});
});
}

View File

@ -67,7 +67,9 @@ export class AddonNotesListComponent implements OnInit, OnDestroy {
*/
ngOnInit(): void {
this.fetchNotes(true).then(() => {
this.notesProvider.logView(this.courseId);
this.notesProvider.logView(this.courseId).catch(() => {
// Ignore errors.
});
});
}
@ -128,7 +130,9 @@ export class AddonNotesListComponent implements OnInit, OnDestroy {
this.refreshIcon = 'spinner';
this.syncIcon = 'spinner';
this.fetchNotes(true).then(() => {
this.notesProvider.logView(this.courseId);
this.notesProvider.logView(this.courseId).catch(() => {
// Ignore errors.
});
});
}

View File

@ -1630,6 +1630,7 @@
"core.view": "View",
"core.viewcode": "View code",
"core.vieweditor": "View editor",
"core.viewembeddedcontent": "View embedded content",
"core.viewprofile": "View profile",
"core.warningofflinedatadeleted": "Offline data from {{component}} '{{name}}' has been deleted. {{error}}",
"core.whatisyourage": "What is your age?",

View File

@ -101,8 +101,13 @@ export class CoreCourseActivityPrefetchHandlerBase extends CoreCourseModulePrefe
}
const prefetchPromise = this.setDownloading(module.id, siteId).then(() => {
// Package marked as downloading, call the download function.
// Send all the params except downloadFn. This includes all params passed after siteId.
// Package marked as downloading, get module info to be able to handle links.
return Promise.all([
this.courseProvider.getModuleBasicInfo(module.id, siteId),
this.courseProvider.getModule(module.id, courseId, undefined, false, true, siteId),
]);
}).then(() => {
// Call the download function, send all the params except downloadFn. This includes all params passed after siteId.
return downloadFn.apply(downloadFn, [module, courseId, single, siteId].concat(args));
}).then((extra: any) => {
// Only accept string types.

View File

@ -61,8 +61,11 @@ export class CoreCourseResourcePrefetchHandlerBase extends CoreCourseModulePrefe
return this.getOngoingDownload(module.id, siteId);
}
// Load module contents (ignore cache so we always have the latest data).
const prefetchPromise = this.loadContents(module, courseId, true).then(() => {
// Get module info to be able to handle links.
const prefetchPromise = this.courseProvider.getModuleBasicInfo(module.id, siteId).then(() => {
// Load module contents (ignore cache so we always have the latest data).
return this.loadContents(module, courseId, true);
}).then(() => {
// Get the intro files.
return this.getIntroFiles(module, courseId);
}).then((introFiles) => {

View File

@ -303,35 +303,18 @@ export class CoreCourseProvider {
siteId?: string, modName?: string): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
let promise;
if (!courseId) {
// No courseId passed, try to retrieve it.
promise = this.getModuleBasicInfo(moduleId, siteId).then((module) => {
return module.course;
});
} else {
promise = Promise.resolve(courseId);
}
return promise.then((cid) => {
courseId = cid;
// Get the site.
return this.sitesProvider.getSite(siteId);
}).then((site) => {
// We have courseId, we can use core_course_get_contents for compatibility.
this.logger.debug(`Getting module ${moduleId} in course ${courseId}`);
// Helper function to do the WS request without processing the result.
const doRequest = (site: CoreSite, moduleId: number, modName: string, includeStealth: boolean, preferCache: boolean):
Promise<any> => {
const params: any = {
courseid: courseId,
options: []
},
preSets: any = {
omitExpires: preferCache
};
courseid: courseId,
options: []
};
const preSets: CoreSiteWSPreSets = {
omitExpires: preferCache
};
if (this.canRequestStealthModules(site)) {
if (includeStealth) {
params.options.push({
name: 'includestealthmodules',
value: 1
@ -354,35 +337,72 @@ export class CoreCourseProvider {
}
if (!preferCache && ignoreCache) {
preSets.getFromCache = 0;
preSets.emergencyCache = 0;
}
if (sectionId) {
params.options.push({
name: 'sectionid',
value: sectionId
});
preSets.getFromCache = false;
preSets.emergencyCache = false;
}
return site.read('core_course_get_contents', params, preSets).catch(() => {
// Error getting the module. Try to get all contents (without filtering by module).
return this.getSections(courseId, false, false, preSets, siteId);
}).then((sections) => {
for (let i = 0; i < sections.length; i++) {
const section = sections[i];
for (let j = 0; j < section.modules.length; j++) {
const module = section.modules[j];
if (module.id == moduleId) {
module.course = courseId;
return module;
}
// The module might still be cached by a request with different parameters.
if (!ignoreCache && !this.appProvider.isOnline()) {
if (includeStealth) {
// Older versions didn't include the includestealthmodules option.
return doRequest(site, moduleId, modName, false, true);
} else if (modName) {
// Falback to the request for the given moduleId only.
return doRequest(site, moduleId, undefined, this.canRequestStealthModules(site), true);
}
}
return Promise.reject(null);
});
};
let promise;
if (!courseId) {
// No courseId passed, try to retrieve it.
promise = this.getModuleBasicInfo(moduleId, siteId).then((module) => {
courseId = module.course;
});
} else {
promise = Promise.resolve();
}
return promise.then(() => {
return this.sitesProvider.getSite(siteId);
}).then((site) => {
// We have courseId, we can use core_course_get_contents for compatibility.
this.logger.debug(`Getting module ${moduleId} in course ${courseId}`);
return doRequest(site, moduleId, modName, this.canRequestStealthModules(site), preferCache);
}).catch(() => {
// Error getting the module. Try to get all contents (without filtering by module).
const preSets: CoreSiteWSPreSets = {
omitExpires: preferCache
};
if (!preferCache && ignoreCache) {
preSets.getFromCache = false;
preSets.emergencyCache = false;
}
return this.getSections(courseId, false, false, preSets, siteId);
}).then((sections) => {
for (let i = 0; i < sections.length; i++) {
const section = sections[i];
if (sectionId != null && !isNaN(sectionId) && sectionId != section.id) {
continue;
}
for (let j = 0; j < section.modules.length; j++) {
const module = section.modules[j];
if (module.id == moduleId) {
module.course = courseId;
return module;
}
}
}
return Promise.reject(null);
});
}

View File

@ -53,7 +53,9 @@ export class CoreGradesCourseComponent {
}
// Add log in Moodle.
return this.gradesProvider.logCourseGradesView(this.courseId, this.userId);
return this.gradesProvider.logCourseGradesView(this.courseId, this.userId).catch(() => {
// Ignore errors.
});
}).finally(() => {
this.gradesLoaded = true;
});

View File

@ -55,7 +55,9 @@ export class CoreGradesCoursesPage {
}
// Add log in Moodle.
return this.gradesProvider.logCoursesGradesView();
return this.gradesProvider.logCoursesGradesView().catch(() => {
// Ignore errors.
});
}).finally(() => {
this.gradesLoaded = true;
});

View File

@ -240,6 +240,7 @@
"view": "View",
"viewcode": "View code",
"vieweditor": "View editor",
"viewembeddedcontent": "View embedded content",
"viewprofile": "View profile",
"warningofflinedatadeleted": "Offline data from {{component}} '{{name}}' has been deleted. {{error}}",
"whatisyourage": "What is your age?",

View File

@ -13,7 +13,7 @@
// limitations under the License.
import { Injectable, NgZone } from '@angular/core';
import { Platform } from 'ionic-angular';
import { Config, Platform } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { Network } from '@ionic-native/network';
import { CoreAppProvider } from '../app';
@ -24,6 +24,7 @@ import { CoreDomUtilsProvider } from './dom';
import { CoreTextUtilsProvider } from './text';
import { CoreUrlUtilsProvider } from './url';
import { CoreUtilsProvider } from './utils';
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
/*
* "Utils" service with helper functions for iframes, embed and similar.
@ -37,7 +38,8 @@ export class CoreIframeUtilsProvider {
constructor(logger: CoreLoggerProvider, private fileProvider: CoreFileProvider, private sitesProvider: CoreSitesProvider,
private urlUtils: CoreUrlUtilsProvider, private textUtils: CoreTextUtilsProvider, private utils: CoreUtilsProvider,
private domUtils: CoreDomUtilsProvider, private platform: Platform, private appProvider: CoreAppProvider,
private translate: TranslateService, private network: Network, private zone: NgZone) {
private translate: TranslateService, private network: Network, private zone: NgZone, private config: Config,
private contentLinksHelper: CoreContentLinksHelperProvider) {
this.logger = logger.getInstance('CoreUtilsProvider');
}
@ -57,16 +59,51 @@ export class CoreIframeUtilsProvider {
return true;
}
// The frame has an online URL but the app is offline. Show a warning.
// The frame has an online URL but the app is offline. Show a warning, or a link if the URL can be opened in the app.
const div = document.createElement('div');
div.setAttribute('text-center', '');
div.setAttribute('padding', '');
div.classList.add('core-iframe-offline-warning');
div.innerHTML = (isSubframe ? '' : this.domUtils.getConnectionWarningIconHtml()) +
'<p>' + this.translate.instant('core.networkerroriframemsg') + '</p>';
element.parentElement.insertBefore(div, element);
const site = this.sitesProvider.getCurrentSite();
const username = site ? site.getInfo().username : undefined;
this.contentLinksHelper.canHandleLink(src, undefined, username).then((canHandleLink) => {
if (canHandleLink) {
const link = document.createElement('a');
if (isSubframe) {
// Ionic styles are not available in subframes, adding some minimal inline styles.
link.style.display = 'block';
link.style.padding = '1em';
link.style.fontWeight = '500';
link.style.textAlign = 'center';
link.style.textTransform = 'uppercase';
link.style.cursor = 'pointer';
} else {
const mode = this.config.get('mode');
link.setAttribute('ion-button', '');
link.classList.add('button', 'button-' + mode,
'button-default', 'button-default-' + mode,
'button-block', 'button-block-' + mode);
}
const message = this.translate.instant('core.viewembeddedcontent');
link.innerHTML = isSubframe ? message : '<span class="button-inner">' + message + '</span>';
link.onclick = (event: Event): void => {
this.contentLinksHelper.handleLink(src, username);
event.preventDefault();
};
div.appendChild(link);
} else {
div.innerHTML = (isSubframe ? '' : this.domUtils.getConnectionWarningIconHtml()) +
'<p>' + this.translate.instant('core.networkerroriframemsg') + '</p>';
}
element.parentElement.insertBefore(div, element);
});
// Add a class to specify that the iframe is hidden.
element.classList.add('core-iframe-offline-disabled');