Merge pull request #2310 from dpalou/MOBILE-3298

MOBILE-3298 course: Don't fail module download if avatar fails
main
Juan Leyva 2020-03-09 14:08:07 +01:00 committed by GitHub
commit 14a963cf6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 73 additions and 61 deletions

View File

@ -354,15 +354,7 @@ export class AddonModAssignPrefetchHandler extends CoreCourseActivityPrefetchHan
}).then(() => { }).then(() => {
// Participiants already fetched, we don't need to ignore cache now. // Participiants already fetched, we don't need to ignore cache now.
return this.assignHelper.getParticipants(assign, group.id, false, siteId).then((participants) => { return this.assignHelper.getParticipants(assign, group.id, false, siteId).then((participants) => {
const promises = []; return this.userProvider.prefetchUserAvatars(participants, 'profileimageurl', siteId);
participants.forEach((participant) => {
if (participant.profileimageurl) {
promises.push(this.filepoolProvider.addToQueueByUrl(siteId, participant.profileimageurl));
}
});
return Promise.all(promises);
}).catch(() => { }).catch(() => {
// Fail silently (Moodle < 3.2). // Fail silently (Moodle < 3.2).
}); });

View File

@ -182,9 +182,7 @@ export class AddonModChatPrefetchHandler extends CoreCourseActivityPrefetchHandl
}); });
const userIds = Object.keys(users).map(Number); const userIds = Object.keys(users).map(Number);
return this.userProvider.prefetchProfiles(userIds, courseId, siteId).catch(() => { return this.userProvider.prefetchProfiles(userIds, courseId, siteId);
// Ignore errors, some users might not exist.
});
}); });
} }
} }

View File

@ -244,13 +244,14 @@ export class AddonModForumProvider {
* Check if a user can post to all groups. * Check if a user can post to all groups.
* *
* @param forumId Forum ID. * @param forumId Forum ID.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with an object with the following properties: * @return Promise resolved with an object with the following properties:
* - status (boolean) * - status (boolean)
* - canpindiscussions (boolean) * - canpindiscussions (boolean)
* - cancreateattachment (boolean) * - cancreateattachment (boolean)
*/ */
canAddDiscussionToAll(forumId: number): Promise<any> { canAddDiscussionToAll(forumId: number, siteId?: string): Promise<any> {
return this.canAddDiscussion(forumId, AddonModForumProvider.ALL_PARTICIPANTS); return this.canAddDiscussion(forumId, AddonModForumProvider.ALL_PARTICIPANTS, siteId);
} }
/** /**

View File

@ -107,12 +107,13 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
* Get the posts to be prefetched. * Get the posts to be prefetched.
* *
* @param forum Forum instance. * @param forum Forum instance.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with array of posts. * @return Promise resolved with array of posts.
*/ */
protected getPostsForPrefetch(forum: any): Promise<any[]> { protected getPostsForPrefetch(forum: any, siteId?: string): Promise<any[]> {
const promises = this.forumProvider.getAvailableSortOrders().map((sortOrder) => { const promises = this.forumProvider.getAvailableSortOrders().map((sortOrder) => {
// Get discussions in first 2 pages. // Get discussions in first 2 pages.
return this.forumProvider.getDiscussionsInPages(forum.id, sortOrder.value, false, 2).then((response) => { return this.forumProvider.getDiscussionsInPages(forum.id, sortOrder.value, false, 2, 0, siteId).then((response) => {
if (response.error) { if (response.error) {
return Promise.reject(null); return Promise.reject(null);
} }
@ -120,7 +121,7 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
const promises = []; const promises = [];
response.discussions.forEach((discussion) => { response.discussions.forEach((discussion) => {
promises.push(this.forumProvider.getDiscussionPosts(discussion.discussion)); promises.push(this.forumProvider.getDiscussionPosts(discussion.discussion, siteId));
}); });
return Promise.all(promises); return Promise.all(promises);
@ -200,41 +201,31 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
*/ */
protected prefetchForum(module: any, courseId: number, single: boolean, siteId: string): Promise<any> { protected prefetchForum(module: any, courseId: number, single: boolean, siteId: string): Promise<any> {
// Get the forum data. // Get the forum data.
return this.forumProvider.getForum(courseId, module.id).then((forum) => { return this.forumProvider.getForum(courseId, module.id, siteId).then((forum) => {
const promises = []; const promises = [];
// Prefetch the posts. // Prefetch the posts.
promises.push(this.getPostsForPrefetch(forum).then((posts) => { promises.push(this.getPostsForPrefetch(forum, siteId).then((posts) => {
const promises = []; const promises = [];
// Gather user profile images. const files = this.getIntroFilesFromInstance(module, forum).concat(this.getPostsFiles(posts));
const avatars = {}; // List of user avatars, preventing duplicates.
posts.forEach((post) => {
if (post.userpictureurl) {
avatars[post.userpictureurl] = true;
}
});
// Prefetch intro files, attachments, embedded files and user avatars.
const avatarFiles = Object.keys(avatars).map((url) => {
return { fileurl: url };
});
const files = this.getIntroFilesFromInstance(module, forum).concat(this.getPostsFiles(posts)).concat(avatarFiles);
promises.push(this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id)); promises.push(this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id));
// Prefetch groups data. // Prefetch groups data.
promises.push(this.prefetchGroupsInfo(forum, courseId, forum.cancreatediscussions)); promises.push(this.prefetchGroupsInfo(forum, courseId, forum.cancreatediscussions, siteId));
// Prefetch avatars.
promises.push(this.userProvider.prefetchUserAvatars(posts, 'userpictureurl', siteId));
return Promise.all(promises); return Promise.all(promises);
})); }));
// Prefetch access information. // Prefetch access information.
promises.push(this.forumProvider.getAccessInformation(forum.id)); promises.push(this.forumProvider.getAccessInformation(forum.id, false, siteId));
// Prefetch sort order preference. // Prefetch sort order preference.
if (this.forumProvider.isDiscussionListSortingAvailable()) { if (this.forumProvider.isDiscussionListSortingAvailable()) {
promises.push(this.userProvider.getUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER)); promises.push(this.userProvider.getUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER, siteId));
} }
return Promise.all(promises); return Promise.all(promises);
@ -247,30 +238,31 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
* @param module The module object returned by WS. * @param module The module object returned by WS.
* @param courseI Course ID the module belongs to. * @param courseI Course ID the module belongs to.
* @param canCreateDiscussions Whether the user can create discussions in the forum. * @param canCreateDiscussions Whether the user can create discussions in the forum.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved when group data has been prefetched. * @return Promise resolved when group data has been prefetched.
*/ */
protected prefetchGroupsInfo(forum: any, courseId: number, canCreateDiscussions: boolean): any { protected prefetchGroupsInfo(forum: any, courseId: number, canCreateDiscussions: boolean, siteId?: string): any {
// Check group mode. // Check group mode.
return this.groupsProvider.getActivityGroupMode(forum.cmid).then((mode) => { return this.groupsProvider.getActivityGroupMode(forum.cmid, siteId).then((mode) => {
if (mode !== CoreGroupsProvider.SEPARATEGROUPS && mode !== CoreGroupsProvider.VISIBLEGROUPS) { if (mode !== CoreGroupsProvider.SEPARATEGROUPS && mode !== CoreGroupsProvider.VISIBLEGROUPS) {
// Activity doesn't use groups. Prefetch canAddDiscussionToAll to determine if user can pin/attach. // Activity doesn't use groups. Prefetch canAddDiscussionToAll to determine if user can pin/attach.
return this.forumProvider.canAddDiscussionToAll(forum.id).catch(() => { return this.forumProvider.canAddDiscussionToAll(forum.id, siteId).catch(() => {
// Ignore errors. // Ignore errors.
}); });
} }
// Activity uses groups, prefetch allowed groups. // Activity uses groups, prefetch allowed groups.
return this.groupsProvider.getActivityAllowedGroups(forum.cmid).then((result) => { return this.groupsProvider.getActivityAllowedGroups(forum.cmid, undefined, siteId).then((result) => {
if (mode === CoreGroupsProvider.SEPARATEGROUPS) { if (mode === CoreGroupsProvider.SEPARATEGROUPS) {
// Groups are already filtered by WS. Prefetch canAddDiscussionToAll to determine if user can pin/attach. // Groups are already filtered by WS. Prefetch canAddDiscussionToAll to determine if user can pin/attach.
return this.forumProvider.canAddDiscussionToAll(forum.id).catch(() => { return this.forumProvider.canAddDiscussionToAll(forum.id, siteId).catch(() => {
// Ignore errors. // Ignore errors.
}); });
} }
if (canCreateDiscussions) { if (canCreateDiscussions) {
// Prefetch data to check the visible groups when creating discussions. // Prefetch data to check the visible groups when creating discussions.
return this.forumProvider.canAddDiscussionToAll(forum.id).catch(() => { return this.forumProvider.canAddDiscussionToAll(forum.id, siteId).catch(() => {
// The call failed, let's assume he can't. // The call failed, let's assume he can't.
return { return {
status: false status: false
@ -284,7 +276,7 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
// The user can't post to all groups, let's check which groups he can post to. // The user can't post to all groups, let's check which groups he can post to.
const groupPromises = []; const groupPromises = [];
result.groups.forEach((group) => { result.groups.forEach((group) => {
groupPromises.push(this.forumProvider.canAddDiscussion(forum.id, group.id).catch(() => { groupPromises.push(this.forumProvider.canAddDiscussion(forum.id, group.id, siteId).catch(() => {
// Ignore errors. // Ignore errors.
})); }));
}); });

View File

@ -26,6 +26,7 @@ import { AddonModGlossaryProvider } from './glossary';
import { AddonModGlossarySyncProvider } from './sync'; import { AddonModGlossarySyncProvider } from './sync';
import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; import { CoreFilterHelperProvider } from '@core/filter/providers/helper';
import { CorePluginFileDelegate } from '@providers/plugin-file-delegate'; import { CorePluginFileDelegate } from '@providers/plugin-file-delegate';
import { CoreUserProvider } from '@core/user/providers/user';
/** /**
* Handler to prefetch forums. * Handler to prefetch forums.
@ -48,7 +49,8 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH
pluginFileDelegate: CorePluginFileDelegate, pluginFileDelegate: CorePluginFileDelegate,
protected glossaryProvider: AddonModGlossaryProvider, protected glossaryProvider: AddonModGlossaryProvider,
protected commentsProvider: CoreCommentsProvider, protected commentsProvider: CoreCommentsProvider,
protected syncProvider: AddonModGlossarySyncProvider) { protected syncProvider: AddonModGlossarySyncProvider,
protected userProvider: CoreUserProvider) {
super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper, super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper,
pluginFileDelegate); pluginFileDelegate);
@ -165,35 +167,29 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH
// Fetch all entries to get information from. // Fetch all entries to get information from.
promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByLetter, promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByLetter,
[glossary.id, 'ALL'], false, false, siteId).then((entries) => { [glossary.id, 'ALL'], false, false, siteId).then((entries) => {
const promises = [], const promises = [];
commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite(), const commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite();
avatars = {}; // List of user avatars, preventing duplicates.
entries.forEach((entry) => { entries.forEach((entry) => {
// Don't fetch individual entries, it's too many WS calls. // Don't fetch individual entries, it's too many WS calls.
if (entry.userpictureurl) {
avatars[entry.userpictureurl] = true;
}
if (glossary.allowcomments && commentsEnabled) { if (glossary.allowcomments && commentsEnabled) {
promises.push(this.commentsProvider.getComments('module', glossary.coursemodule, 'mod_glossary', entry.id, promises.push(this.commentsProvider.getComments('module', glossary.coursemodule, 'mod_glossary', entry.id,
'glossary_entry', 0, siteId)); 'glossary_entry', 0, siteId));
} }
}); });
// Prefetch intro files, entries files and user avatars. const files = this.getFilesFromGlossaryAndEntries(module, glossary, entries);
const avatarFiles = Object.keys(avatars).map((url) => {
return { fileurl: url };
});
const files = this.getFilesFromGlossaryAndEntries(module, glossary, entries).concat(avatarFiles);
promises.push(this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id)); promises.push(this.filepoolProvider.addFilesToQueue(siteId, files, this.component, module.id));
// Prefetch user avatars.
promises.push(this.userProvider.prefetchUserAvatars(entries, 'userpictureurl', siteId));
return Promise.all(promises); return Promise.all(promises);
})); }));
// Get all categories. // Get all categories.
promises.push(this.glossaryProvider.getAllCategories(glossary.id)); promises.push(this.glossaryProvider.getAllCategories(glossary.id, siteId));
// Prefetch data for link handlers. // Prefetch data for link handlers.
promises.push(this.courseProvider.getModuleBasicInfo(module.id, siteId)); promises.push(this.courseProvider.getModuleBasicInfo(module.id, siteId));

View File

@ -375,9 +375,7 @@ export class AddonModWorkshopPrefetchHandler extends CoreCourseActivityPrefetchH
}); });
}).then(() => { }).then(() => {
// Prefetch user profiles. // Prefetch user profiles.
return this.userProvider.prefetchProfiles(userIds, courseId, siteId).catch(() => { return this.userProvider.prefetchProfiles(userIds, courseId, siteId);
// Ignore errors.
});
}); });
} }

View File

@ -522,8 +522,10 @@ export class CoreUserProvider {
promises.push(this.getProfile(userId, courseId, false, siteId).then((profile) => { promises.push(this.getProfile(userId, courseId, false, siteId).then((profile) => {
if (profile.profileimageurl) { if (profile.profileimageurl) {
this.filepoolProvider.addToQueueByUrl(siteId, profile.profileimageurl); return this.filepoolProvider.addToQueueByUrl(siteId, profile.profileimageurl);
} }
}).catch((error) => {
this.logger.warn(`Ignore error when prefetching user ${userId}`, error);
})); }));
} }
}); });
@ -531,6 +533,39 @@ export class CoreUserProvider {
return Promise.all(promises); return Promise.all(promises);
} }
/**
* Prefetch user avatars. It prevents duplicates.
*
* @param entries List of entries that have the images.
* @param propertyName The name of the property that contains the image.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved when prefetched.
*/
async prefetchUserAvatars(entries: any[], propertyName: string, siteId?: string): Promise<void> {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
const treated = {};
const promises = entries.map(async (entry) => {
const imageUrl = entry[propertyName];
if (!imageUrl || treated[imageUrl]) {
// It doesn't have an image or it has already been treated.
return;
}
treated[imageUrl] = true;
try {
await this.filepoolProvider.addToQueueByUrl(siteId, imageUrl);
} catch (ex) {
this.logger.warn(`Ignore error when prefetching user avatar ${imageUrl}`, entry, ex);
}
});
await Promise.all(promises);
}
/** /**
* Search participants in a certain course. * Search participants in a certain course.
* *