forked from CIT/Vmeda.Online
		
	MOBILE-3643 forum: Migrate sync
This commit is contained in:
		
							parent
							
								
									d41523d4bb
								
							
						
					
					
						commit
						41259a66c9
					
				@ -3,7 +3,7 @@
 | 
			
		||||
    <core-context-menu>
 | 
			
		||||
        <core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" [href]="externalUrl" iconAction="open"></core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" (action)="expandDescription()" [iconAction]="'arrow-forward'"></core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}" [iconAction]="'fa-newspaper-o'" (action)="gotoBlog($event)"></core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}" [iconAction]="'fa-newspaper-o'" (action)="gotoBlog()"></core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item *ngIf="discussions.loaded && !(hasOffline || hasOfflineRatings) && isOnline" [priority]="700" [content]="'addon.mod_forum.refreshdiscussions' | translate" (action)="doRefresh(null, $event)" [iconAction]="refreshIcon" [closeOnClick]="false"></core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item *ngIf="discussions.loaded && (hasOffline || hasOfflineRatings) && isOnline"  [priority]="600" [content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(null, $event, true)" [iconAction]="syncIcon" [closeOnClick]="false"></core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="500" [content]="prefetchText" (action)="prefetch($event)" [iconAction]="prefetchStatusIcon" [closeOnClick]="false"></core-context-menu-item>
 | 
			
		||||
@ -103,6 +103,9 @@
 | 
			
		||||
                    </ion-row>
 | 
			
		||||
                </ion-label>
 | 
			
		||||
            </ion-item>
 | 
			
		||||
 | 
			
		||||
            <core-infinite-loading [enabled]="discussions.onlineLoaded && !discussions.completed" (action)="fetchMoreDiscussions($event)" [error]="fetchMoreDiscussionsFailed">
 | 
			
		||||
            </core-infinite-loading>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,12 @@ import { CoreCourseContentsPage } from '@features/course/pages/contents/contents
 | 
			
		||||
import { AddonModForumHelper } from '@addons/mod/forum/services/helper.service';
 | 
			
		||||
import { CoreGroups, CoreGroupsProvider } from '@services/groups';
 | 
			
		||||
import { CoreEvents, CoreEventObserver } from '@singletons/events';
 | 
			
		||||
import { AddonModForumSyncProvider } from '@addons/mod/forum/services/sync.service';
 | 
			
		||||
import {
 | 
			
		||||
    AddonModForumAutoSyncData,
 | 
			
		||||
    AddonModForumManualSyncData,
 | 
			
		||||
    AddonModForumSyncProvider,
 | 
			
		||||
    AddonModForumSyncResult,
 | 
			
		||||
} from '@addons/mod/forum/services/sync.service';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
@ -41,6 +46,7 @@ import { AddonModForumDiscussionOptionsMenuComponent } from '../discussion-optio
 | 
			
		||||
import { AddonModForumSortOrderSelectorComponent } from '../sort-order-selector/sort-order-selector';
 | 
			
		||||
import { CoreScreen } from '@services/screen';
 | 
			
		||||
import { CoreArray } from '@singletons/array';
 | 
			
		||||
import { AddonModForumPrefetchHandler } from '../../services/handlers/prefetch';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays a forum entry page.
 | 
			
		||||
@ -58,8 +64,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
    moduleName = 'forum';
 | 
			
		||||
    descriptionNote?: string;
 | 
			
		||||
    forum?: AddonModForumData;
 | 
			
		||||
    canLoadMore = false;
 | 
			
		||||
    loadMoreError = false;
 | 
			
		||||
    fetchMoreDiscussionsFailed = false;
 | 
			
		||||
    discussions: AddonModForumDiscussionsManager;
 | 
			
		||||
    canAddDiscussion = false;
 | 
			
		||||
    addDiscussionText!: string;
 | 
			
		||||
@ -201,12 +206,12 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
     * @param sync If the refresh needs syncing.
 | 
			
		||||
     * @param showErrors Wether to show errors to the user or hide them.
 | 
			
		||||
     */
 | 
			
		||||
    protected async fetchContent(refresh: boolean = false, sync: boolean = false): Promise<void> {
 | 
			
		||||
        this.loadMoreError = false;
 | 
			
		||||
    protected async fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<void> {
 | 
			
		||||
        this.fetchMoreDiscussionsFailed = false;
 | 
			
		||||
 | 
			
		||||
        const promises: Promise<void>[] = [];
 | 
			
		||||
 | 
			
		||||
        promises.push(this.fetchForum());
 | 
			
		||||
        promises.push(this.fetchForum(sync, showErrors));
 | 
			
		||||
        promises.push(this.fetchSortOrderPreference());
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
@ -219,7 +224,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
            if (refresh) {
 | 
			
		||||
                CoreDomUtils.instance.showErrorModalDefault(error, 'addon.mod_forum.errorgetforum', true);
 | 
			
		||||
 | 
			
		||||
                this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
 | 
			
		||||
                this.fetchMoreDiscussionsFailed = true; // Set to prevent infinite calls with infinite-loading.
 | 
			
		||||
            } else {
 | 
			
		||||
                // Get forum failed, retry without using cache since it might be a new activity.
 | 
			
		||||
                await this.refreshContent(sync);
 | 
			
		||||
@ -229,136 +234,102 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
        this.fillContextMenu(refresh);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async fetchForum(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<void> {
 | 
			
		||||
    private async fetchForum(sync: boolean = false, showErrors: boolean = false): Promise<void> {
 | 
			
		||||
        if (!this.courseId || !this.module) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.loadMoreError = false;
 | 
			
		||||
        const forum = await AddonModForum.instance.getForum(this.courseId, this.module.id);
 | 
			
		||||
 | 
			
		||||
        this.forum = forum;
 | 
			
		||||
        this.description = forum.intro || this.description;
 | 
			
		||||
        this.availabilityMessage = AddonModForumHelper.instance.getAvailabilityMessage(forum);
 | 
			
		||||
        this.descriptionNote = Translate.instant('addon.mod_forum.numdiscussions', {
 | 
			
		||||
            numdiscussions: forum.numdiscussions,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (typeof forum.istracked != 'undefined') {
 | 
			
		||||
            this.trackPosts = forum.istracked;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.dataRetrieved.emit(forum);
 | 
			
		||||
 | 
			
		||||
        switch (forum.type) {
 | 
			
		||||
            case 'news':
 | 
			
		||||
            case 'blog':
 | 
			
		||||
                this.addDiscussionText = Translate.instant('addon.mod_forum.addanewtopic');
 | 
			
		||||
                break;
 | 
			
		||||
            case 'qanda':
 | 
			
		||||
                this.addDiscussionText = Translate.instant('addon.mod_forum.addanewquestion');
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                this.addDiscussionText = Translate.instant('addon.mod_forum.addanewdiscussion');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (sync) {
 | 
			
		||||
            // Try to synchronize the forum.
 | 
			
		||||
            const updated = await this.syncActivity(showErrors);
 | 
			
		||||
 | 
			
		||||
            if (updated) {
 | 
			
		||||
                // Sync successful, send event.
 | 
			
		||||
                CoreEvents.trigger<AddonModForumManualSyncData>(AddonModForumSyncProvider.MANUAL_SYNCED, {
 | 
			
		||||
                    forumId: forum.id,
 | 
			
		||||
                    userId: CoreSites.instance.getCurrentSiteUserId(),
 | 
			
		||||
                    source: 'index',
 | 
			
		||||
                }, CoreSites.instance.getCurrentSiteId());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const promises: Promise<void>[] = [];
 | 
			
		||||
 | 
			
		||||
        // Check if the activity uses groups.
 | 
			
		||||
        promises.push(
 | 
			
		||||
            AddonModForum.instance
 | 
			
		||||
                .getForum(this.courseId, this.module.id)
 | 
			
		||||
                .then(async (forum) => {
 | 
			
		||||
                    this.forum = forum;
 | 
			
		||||
                    this.description = forum.intro || this.description;
 | 
			
		||||
                    this.availabilityMessage = AddonModForumHelper.instance.getAvailabilityMessage(forum);
 | 
			
		||||
                    this.descriptionNote = Translate.instant('addon.mod_forum.numdiscussions', {
 | 
			
		||||
                        numdiscussions: forum.numdiscussions,
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    if (typeof forum.istracked != 'undefined') {
 | 
			
		||||
                        this.trackPosts = forum.istracked;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    this.dataRetrieved.emit(forum);
 | 
			
		||||
 | 
			
		||||
                    switch (forum.type) {
 | 
			
		||||
                        case 'news':
 | 
			
		||||
                        case 'blog':
 | 
			
		||||
                            this.addDiscussionText = Translate.instant('addon.mod_forum.addanewtopic');
 | 
			
		||||
                            break;
 | 
			
		||||
                        case 'qanda':
 | 
			
		||||
                            this.addDiscussionText = Translate.instant('addon.mod_forum.addanewquestion');
 | 
			
		||||
                            break;
 | 
			
		||||
                        default:
 | 
			
		||||
                            this.addDiscussionText = Translate.instant('addon.mod_forum.addanewdiscussion');
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (sync) {
 | 
			
		||||
                        // Try to synchronize the forum.
 | 
			
		||||
                        const updated = await this.syncActivity(showErrors);
 | 
			
		||||
 | 
			
		||||
                        if (updated) {
 | 
			
		||||
                            // Sync successful, send event.
 | 
			
		||||
                            CoreEvents.trigger(AddonModForumSyncProvider.MANUAL_SYNCED, {
 | 
			
		||||
                                forumId: forum.id,
 | 
			
		||||
                                userId: CoreSites.instance.getCurrentSiteUserId(),
 | 
			
		||||
                                source: 'index',
 | 
			
		||||
                            }, CoreSites.instance.getCurrentSiteId());
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    const promises: Promise<void>[] = [];
 | 
			
		||||
 | 
			
		||||
                    // Check if the activity uses groups.
 | 
			
		||||
                    promises.push(
 | 
			
		||||
                        // eslint-disable-next-line promise/no-nesting
 | 
			
		||||
                        CoreGroups.instance
 | 
			
		||||
                            .getActivityGroupMode(this.forum.cmid)
 | 
			
		||||
                            .then(async mode => {
 | 
			
		||||
                                this.usesGroups = mode === CoreGroupsProvider.SEPARATEGROUPS
 | 
			
		||||
            CoreGroups.instance
 | 
			
		||||
                .getActivityGroupMode(this.forum.cmid)
 | 
			
		||||
                .then(async mode => {
 | 
			
		||||
                    this.usesGroups = mode === CoreGroupsProvider.SEPARATEGROUPS
 | 
			
		||||
                                    || mode === CoreGroupsProvider.VISIBLEGROUPS;
 | 
			
		||||
 | 
			
		||||
                                return;
 | 
			
		||||
                            }),
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    promises.push(
 | 
			
		||||
                        // eslint-disable-next-line promise/no-nesting
 | 
			
		||||
                        AddonModForum.instance
 | 
			
		||||
                            .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.
 | 
			
		||||
                                // Just in case the forum was fetched from WS when the cut-off date was not reached but it is now.
 | 
			
		||||
                                const cutoffDateReached = AddonModForumHelper.instance.isCutoffDateReached(this.forum!)
 | 
			
		||||
                                    && !accessInfo.cancanoverridecutoff;
 | 
			
		||||
                                this.canAddDiscussion = !!this.forum?.cancreatediscussions && !cutoffDateReached;
 | 
			
		||||
 | 
			
		||||
                                return;
 | 
			
		||||
                            }),
 | 
			
		||||
                    );
 | 
			
		||||
 | 
			
		||||
                    if (AddonModForum.instance.isSetPinStateAvailableForSite()) {
 | 
			
		||||
                        // Use the canAddDiscussion WS to check if the user can pin discussions.
 | 
			
		||||
                        promises.push(
 | 
			
		||||
                            // eslint-disable-next-line promise/no-nesting
 | 
			
		||||
                            AddonModForum.instance
 | 
			
		||||
                                .canAddDiscussionToAll(this.forum.id, { cmId: this.module!.id })
 | 
			
		||||
                                .then(async response => {
 | 
			
		||||
                                    this.canPin = !!response.canpindiscussions;
 | 
			
		||||
 | 
			
		||||
                                    return;
 | 
			
		||||
                                })
 | 
			
		||||
                                .catch(async () => {
 | 
			
		||||
                                    this.canPin = false;
 | 
			
		||||
 | 
			
		||||
                                    return;
 | 
			
		||||
                                }),
 | 
			
		||||
                        );
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this.canPin = false;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    await Promise.all(promises);
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        promises.push(this.fetchSortOrderPreference());
 | 
			
		||||
        promises.push(
 | 
			
		||||
            AddonModForum.instance
 | 
			
		||||
                .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.
 | 
			
		||||
                    // Just in case the forum was fetched from WS when the cut-off date was not reached but it is now.
 | 
			
		||||
                    const cutoffDateReached = AddonModForumHelper.instance.isCutoffDateReached(this.forum!)
 | 
			
		||||
                                    && !accessInfo.cancanoverridecutoff;
 | 
			
		||||
                    this.canAddDiscussion = !!this.forum?.cancreatediscussions && !cutoffDateReached;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            await Promise.all(promises);
 | 
			
		||||
            await Promise.all([
 | 
			
		||||
                this.fetchOfflineDiscussions(),
 | 
			
		||||
                this.fetchDiscussions(refresh),
 | 
			
		||||
            ]);
 | 
			
		||||
        } catch (message) {
 | 
			
		||||
            if (!refresh) {
 | 
			
		||||
                // Get forum failed, retry without using cache since it might be a new activity.
 | 
			
		||||
                return this.refreshContent(sync);
 | 
			
		||||
            }
 | 
			
		||||
                    return;
 | 
			
		||||
                }),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
            CoreDomUtils.instance.showErrorModalDefault(message, 'addon.mod_forum.errorgetforum', true);
 | 
			
		||||
        if (AddonModForum.instance.isSetPinStateAvailableForSite()) {
 | 
			
		||||
            // Use the canAddDiscussion WS to check if the user can pin discussions.
 | 
			
		||||
            promises.push(
 | 
			
		||||
                AddonModForum.instance
 | 
			
		||||
                    .canAddDiscussionToAll(this.forum.id, { cmId: this.module!.id })
 | 
			
		||||
                    .then(async response => {
 | 
			
		||||
                        this.canPin = !!response.canpindiscussions;
 | 
			
		||||
 | 
			
		||||
            this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
 | 
			
		||||
                        return;
 | 
			
		||||
                    })
 | 
			
		||||
                    .catch(async () => {
 | 
			
		||||
                        this.canPin = false;
 | 
			
		||||
 | 
			
		||||
                        return;
 | 
			
		||||
                    }),
 | 
			
		||||
            );
 | 
			
		||||
        } else {
 | 
			
		||||
            this.canPin = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.fillContextMenu(refresh);
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -414,7 +385,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
     */
 | 
			
		||||
    protected async fetchDiscussions(refresh: boolean): Promise<void> {
 | 
			
		||||
        const forum = this.forum!;
 | 
			
		||||
        this.loadMoreError = false;
 | 
			
		||||
        this.fetchMoreDiscussionsFailed = false;
 | 
			
		||||
 | 
			
		||||
        if (refresh) {
 | 
			
		||||
            this.page = 0;
 | 
			
		||||
@ -435,7 +406,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
        if (forum.type === 'single') {
 | 
			
		||||
            for (const discussion of discussions) {
 | 
			
		||||
                if (discussion.userfullname && discussion.parent === 0) {
 | 
			
		||||
                    (discussion as any).userfullname = false;
 | 
			
		||||
                    discussion.userfullname = false;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -452,12 +423,11 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.page === 0) {
 | 
			
		||||
            this.discussions.setOnlineDiscussions(discussions);
 | 
			
		||||
            this.discussions.setOnlineDiscussions(discussions, response.canLoadMore);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.discussions.setItems(this.discussions.items.concat(discussions));
 | 
			
		||||
            this.discussions.setItems(this.discussions.items.concat(discussions), response.canLoadMore);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.canLoadMore = response.canLoadMore;
 | 
			
		||||
        this.page++;
 | 
			
		||||
 | 
			
		||||
        // Check if there are replies for discussions stored in offline.
 | 
			
		||||
@ -467,7 +437,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
 | 
			
		||||
        if (hasOffline) {
 | 
			
		||||
            // Only update new fetched discussions.
 | 
			
		||||
            const promises = discussions.map(async (discussion: any) => {
 | 
			
		||||
            const promises = discussions.map(async (discussion) => {
 | 
			
		||||
                // Get offline discussions.
 | 
			
		||||
                const replies = await AddonModForumOffline.instance.getDiscussionReplies(discussion.discussion);
 | 
			
		||||
 | 
			
		||||
@ -484,14 +454,16 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
     * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    fetchMoreDiscussions(infiniteComplete?: any): Promise<any> {
 | 
			
		||||
        return this.fetchDiscussions(false).catch((message) => {
 | 
			
		||||
            CoreDomUtils.instance.showErrorModalDefault(message, 'addon.mod_forum.errorgetforum', true);
 | 
			
		||||
    async fetchMoreDiscussions(complete: () => void): Promise<void> {
 | 
			
		||||
        try {
 | 
			
		||||
            await this.fetchDiscussions(false);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.instance.showErrorModalDefault(error, 'addon.mod_forum.errorgetforum', true);
 | 
			
		||||
 | 
			
		||||
            this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
 | 
			
		||||
        }).finally(() => {
 | 
			
		||||
            infiniteComplete && infiniteComplete();
 | 
			
		||||
        });
 | 
			
		||||
            this.fetchMoreDiscussionsFailed = true;
 | 
			
		||||
        } finally {
 | 
			
		||||
            complete();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -522,7 +494,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
     *
 | 
			
		||||
     * @return Resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected invalidateContent(): Promise<any> {
 | 
			
		||||
    protected async invalidateContent(): Promise<void> {
 | 
			
		||||
        const promises: Promise<void>[] = [];
 | 
			
		||||
 | 
			
		||||
        promises.push(AddonModForum.instance.invalidateForumData(this.courseId!));
 | 
			
		||||
@ -537,7 +509,16 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
            promises.push(CoreUser.instance.invalidateUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Promise.all(promises);
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Performs the sync of the activity.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected sync(): Promise<AddonModForumSyncResult> {
 | 
			
		||||
        return AddonModForumPrefetchHandler.instance.sync(this.module!, this.courseId!);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -546,10 +527,23 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
     * @param result Data returned on the sync function.
 | 
			
		||||
     * @return Whether it succeed or not.
 | 
			
		||||
     */
 | 
			
		||||
    protected hasSyncSucceed(result: any): boolean {
 | 
			
		||||
    protected hasSyncSucceed(result: AddonModForumSyncResult): boolean {
 | 
			
		||||
        return result.updated;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Compares sync event data with current data to check if refresh content is needed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param syncEventData Data receiven on sync observer.
 | 
			
		||||
     * @return True if refresh is needed, false otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    protected isRefreshSyncNeeded(syncEventData: AddonModForumAutoSyncData | AddonModForumManualSyncData): boolean {
 | 
			
		||||
        return !!this.forum
 | 
			
		||||
            && (!('source' in syncEventData) || syncEventData.source != 'index')
 | 
			
		||||
            && syncEventData.forumId == this.forum.id
 | 
			
		||||
            && syncEventData.userId == CoreSites.instance.getCurrentSiteUserId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Function called when we receive an event of new discussion or reply to discussion.
 | 
			
		||||
     *
 | 
			
		||||
@ -685,6 +679,8 @@ type DiscussionItem = AddonModForumDiscussion | AddonModForumOfflineDiscussion |
 | 
			
		||||
 */
 | 
			
		||||
class AddonModForumDiscussionsManager extends CorePageItemsListManager<DiscussionItem> {
 | 
			
		||||
 | 
			
		||||
    onlineLoaded = false;
 | 
			
		||||
 | 
			
		||||
    private discussionsPathPrefix: string;
 | 
			
		||||
    private component: AddonModForumIndexComponent;
 | 
			
		||||
 | 
			
		||||
@ -747,10 +743,10 @@ class AddonModForumDiscussionsManager extends CorePageItemsListManager<Discussio
 | 
			
		||||
     *
 | 
			
		||||
     * @param onlineDiscussions Online discussions
 | 
			
		||||
     */
 | 
			
		||||
    setOnlineDiscussions(onlineDiscussions: AddonModForumDiscussion[]): void {
 | 
			
		||||
    setOnlineDiscussions(onlineDiscussions: AddonModForumDiscussion[], hasMoreItems: boolean = false): void {
 | 
			
		||||
        const otherDiscussions = this.items.filter(discussion => !this.isOnlineDiscussion(discussion));
 | 
			
		||||
 | 
			
		||||
        this.setItems(otherDiscussions.concat(onlineDiscussions));
 | 
			
		||||
        this.setItems(otherDiscussions.concat(onlineDiscussions), hasMoreItems);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -761,7 +757,16 @@ class AddonModForumDiscussionsManager extends CorePageItemsListManager<Discussio
 | 
			
		||||
    setOfflineDiscussions(offlineDiscussions: AddonModForumOfflineDiscussion[]): void {
 | 
			
		||||
        const otherDiscussions = this.items.filter(discussion => !this.isOfflineDiscussion(discussion));
 | 
			
		||||
 | 
			
		||||
        this.setItems((offlineDiscussions as DiscussionItem[]).concat(otherDiscussions));
 | 
			
		||||
        this.setItems((offlineDiscussions as DiscussionItem[]).concat(otherDiscussions), this.hasMoreItems);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    setItems(discussions: DiscussionItem[], hasMoreItems: boolean = false): void {
 | 
			
		||||
        super.setItems(discussions, hasMoreItems);
 | 
			
		||||
 | 
			
		||||
        this.onlineLoaded = this.onlineLoaded || discussions.some(discussion => this.isOnlineDiscussion(discussion));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,10 @@ import { CoreScreen } from '@services/screen';
 | 
			
		||||
import { AddonModForumComponentsModule } from './components/components.module';
 | 
			
		||||
import { AddonModForumModuleHandler, AddonModForumModuleHandlerService } from './services/handlers/module';
 | 
			
		||||
import { SITE_SCHEMA } from './services/offline-db';
 | 
			
		||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
 | 
			
		||||
import { AddonModForumPrefetchHandler } from './services/handlers/prefetch';
 | 
			
		||||
import { CoreCronDelegate } from '@services/cron';
 | 
			
		||||
import { AddonModForumSyncCronHandler } from './services/handlers/sync-cron';
 | 
			
		||||
 | 
			
		||||
const mainMenuRoutes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
@ -77,7 +81,11 @@ const courseContentsRoutes: Routes = conditionalRoutes(
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            useValue: () => CoreCourseModuleDelegate.instance.registerHandler(AddonModForumModuleHandler.instance),
 | 
			
		||||
            useValue: () => {
 | 
			
		||||
                CoreCourseModuleDelegate.instance.registerHandler(AddonModForumModuleHandler.instance);
 | 
			
		||||
                CoreCourseModulePrefetchDelegate.instance.registerHandler(AddonModForumPrefetchHandler.instance);
 | 
			
		||||
                CoreCronDelegate.instance.register(AddonModForumSyncCronHandler.instance);
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,7 @@ import {
 | 
			
		||||
} from '../../services/forum.service';
 | 
			
		||||
import { AddonModForumHelper } from '../../services/helper.service';
 | 
			
		||||
import { AddonModForumOffline } from '../../services/offline.service';
 | 
			
		||||
import { AddonModForumSync, AddonModForumSyncProvider } from '../../services/sync.service';
 | 
			
		||||
import { AddonModForumManualSyncData, AddonModForumSync, AddonModForumSyncProvider } from '../../services/sync.service';
 | 
			
		||||
 | 
			
		||||
type SortType = 'flat-newest' | 'flat-oldest' | 'nested';
 | 
			
		||||
 | 
			
		||||
@ -180,7 +180,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
 | 
			
		||||
        }, CoreSites.instance.getCurrentSiteId());
 | 
			
		||||
 | 
			
		||||
        // Refresh data if this forum discussion is synchronized from discussions list.
 | 
			
		||||
        this.syncManualObserver = CoreEvents.on(AddonModForumSyncProvider.MANUAL_SYNCED, (data: any) => {
 | 
			
		||||
        this.syncManualObserver = CoreEvents.on(AddonModForumSyncProvider.MANUAL_SYNCED, (data: AddonModForumManualSyncData) => {
 | 
			
		||||
            if (data.source != 'discussion' && data.forumId == this.forumId &&
 | 
			
		||||
                    data.userId == CoreSites.instance.getCurrentSiteUserId()) {
 | 
			
		||||
                // Refresh the data.
 | 
			
		||||
@ -541,7 +541,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
 | 
			
		||||
 | 
			
		||||
                    if (result && result.updated) {
 | 
			
		||||
                        // Sync successful, send event.
 | 
			
		||||
                        CoreEvents.trigger(AddonModForumSyncProvider.MANUAL_SYNCED, {
 | 
			
		||||
                        CoreEvents.trigger<AddonModForumManualSyncData>(AddonModForumSyncProvider.MANUAL_SYNCED, {
 | 
			
		||||
                            forumId: this.forumId,
 | 
			
		||||
                            userId: CoreSites.instance.getCurrentSiteUserId(),
 | 
			
		||||
                            source: 'discussion',
 | 
			
		||||
 | 
			
		||||
@ -1401,7 +1401,7 @@ export type AddonModForumDiscussion = {
 | 
			
		||||
    attachments?: CoreWSExternalFile[];
 | 
			
		||||
    totalscore: number; // The post message total score.
 | 
			
		||||
    mailnow: number; // Mail now?.
 | 
			
		||||
    userfullname: string; // Post author full name.
 | 
			
		||||
    userfullname: string | boolean; // Post author full name.
 | 
			
		||||
    usermodifiedfullname: string; // Post modifier full name.
 | 
			
		||||
    userpictureurl: string; // Post author picture.
 | 
			
		||||
    usermodifiedpictureurl: string; // Post modifier picture.
 | 
			
		||||
@ -1452,6 +1452,7 @@ export type AddonModForumPost = {
 | 
			
		||||
    };
 | 
			
		||||
    attachment?: 0 | 1;
 | 
			
		||||
    attachments?: (CoreFileEntry | AddonModForumWSPostAttachment)[];
 | 
			
		||||
    messageinlinefiles?: CoreWSExternalFile[];
 | 
			
		||||
    haswordcount?: boolean; // Haswordcount.
 | 
			
		||||
    wordcount?: number; // Wordcount.
 | 
			
		||||
    tags?: { // Tags.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										353
									
								
								src/addons/mod/forum/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										353
									
								
								src/addons/mod/forum/services/handlers/prefetch.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,353 @@
 | 
			
		||||
// (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 { AddonModForum, AddonModForumData, AddonModForumPost, AddonModForumProvider } from '../forum.service';
 | 
			
		||||
import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
 | 
			
		||||
import { CoreFilepool } from '@services/filepool';
 | 
			
		||||
import { CoreWSExternalFile } from '@services/ws';
 | 
			
		||||
import { CoreCourse, CoreCourseAnyModuleData, CoreCourseCommonModWSOptions } from '@features/course/services/course';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
import { CoreGroups, CoreGroupsProvider } from '@services/groups';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { AddonModForumSync } from '../sync.service';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to prefetch forums.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModForumPrefetchHandlerService extends CoreCourseActivityPrefetchHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModForum';
 | 
			
		||||
    modName = 'forum';
 | 
			
		||||
    component = AddonModForumProvider.COMPONENT;
 | 
			
		||||
    updatesNames = /^configuration$|^.*files$|^discussions$/;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get list of files. If not defined, we'll assume they're in module.contents.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module Module.
 | 
			
		||||
     * @param courseId Course ID the module belongs to.
 | 
			
		||||
     * @param single True if we're downloading a single module, false if we're downloading a whole section.
 | 
			
		||||
     * @return Promise resolved with the list of files.
 | 
			
		||||
     */
 | 
			
		||||
    async getFiles(module: CoreCourseAnyModuleData, courseId: number): Promise<CoreWSExternalFile[]> {
 | 
			
		||||
        try {
 | 
			
		||||
            const forum = await AddonModForum.instance.getForum(courseId, module.id);
 | 
			
		||||
 | 
			
		||||
            const files = this.getIntroFilesFromInstance(module, forum);
 | 
			
		||||
 | 
			
		||||
            // Get posts.
 | 
			
		||||
            const posts = await this.getPostsForPrefetch(forum, { cmId: module.id });
 | 
			
		||||
 | 
			
		||||
            // Add posts attachments and embedded files.
 | 
			
		||||
            files.concat(this.getPostsFiles(posts));
 | 
			
		||||
 | 
			
		||||
            return files;
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // Forum not found, return empty list.
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Given a list of forum posts, return a list with all the files (attachments and embedded files).
 | 
			
		||||
     *
 | 
			
		||||
     * @param posts Forum posts.
 | 
			
		||||
     * @return Files.
 | 
			
		||||
     */
 | 
			
		||||
    protected getPostsFiles(posts: AddonModForumPost[]): CoreWSExternalFile[] {
 | 
			
		||||
        let files: CoreWSExternalFile[] = [];
 | 
			
		||||
        const getInlineFiles = CoreSites.instance.getCurrentSite()?.isVersionGreaterEqualThan('3.2');
 | 
			
		||||
 | 
			
		||||
        posts.forEach((post) => {
 | 
			
		||||
            if (post.attachments && post.attachments.length) {
 | 
			
		||||
                files = files.concat(post.attachments as CoreWSExternalFile[]);
 | 
			
		||||
            }
 | 
			
		||||
            if (getInlineFiles && post.messageinlinefiles && post.messageinlinefiles.length) {
 | 
			
		||||
                files = files.concat(post.messageinlinefiles);
 | 
			
		||||
            } else if (post.message && !getInlineFiles) {
 | 
			
		||||
                files = files.concat(CoreFilepool.instance.extractDownloadableFilesFromHtmlAsFakeFileObjects(post.message));
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return files;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the posts to be prefetched.
 | 
			
		||||
     *
 | 
			
		||||
     * @param forum Forum instance.
 | 
			
		||||
     * @param options Other options.
 | 
			
		||||
     * @return Promise resolved with array of posts.
 | 
			
		||||
     */
 | 
			
		||||
    protected getPostsForPrefetch(
 | 
			
		||||
        forum: AddonModForumData,
 | 
			
		||||
        options: CoreCourseCommonModWSOptions = {},
 | 
			
		||||
    ): Promise<AddonModForumPost[]> {
 | 
			
		||||
        const promises = AddonModForum.instance.getAvailableSortOrders().map((sortOrder) => {
 | 
			
		||||
            // Get discussions in first 2 pages.
 | 
			
		||||
            const discussionsOptions = {
 | 
			
		||||
                sortOrder: sortOrder.value,
 | 
			
		||||
                numPages: 2,
 | 
			
		||||
                ...options, // Include all options.
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return AddonModForum.instance.getDiscussionsInPages(forum.id, discussionsOptions).then((response) => {
 | 
			
		||||
                if (response.error) {
 | 
			
		||||
                    throw new Error('Failed getting discussions');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const promises: Promise<{ posts: AddonModForumPost[] }>[] = [];
 | 
			
		||||
 | 
			
		||||
                response.discussions.forEach((discussion) => {
 | 
			
		||||
                    promises.push(AddonModForum.instance.getDiscussionPosts(discussion.discussion, options));
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                return Promise.all(promises);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return Promise.all(promises).then((results) => {
 | 
			
		||||
            // Each order has returned its own list of posts. Merge all the lists, preventing duplicates.
 | 
			
		||||
            const posts: AddonModForumPost[] = [];
 | 
			
		||||
            const postIds = {}; // To make the array unique.
 | 
			
		||||
 | 
			
		||||
            results.forEach((orderResults) => {
 | 
			
		||||
                orderResults.forEach((orderResult) => {
 | 
			
		||||
                    orderResult.posts.forEach((post) => {
 | 
			
		||||
                        if (!postIds[post.id]) {
 | 
			
		||||
                            postIds[post.id] = true;
 | 
			
		||||
                            posts.push(post);
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            return posts;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidate the prefetched content.
 | 
			
		||||
     *
 | 
			
		||||
     * @param moduleId The module ID.
 | 
			
		||||
     * @param courseId The course ID the module belongs to.
 | 
			
		||||
     * @return Promise resolved when the data is invalidated.
 | 
			
		||||
     */
 | 
			
		||||
    invalidateContent(moduleId: number, courseId: number): Promise<void> {
 | 
			
		||||
        return AddonModForum.instance.invalidateContent(moduleId, courseId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidate WS calls needed to determine module status (usually, to check if module is downloadable).
 | 
			
		||||
     * It doesn't need to invalidate check updates. It should NOT invalidate files nor all the prefetched data.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module Module.
 | 
			
		||||
     * @param courseId Course ID the module belongs to.
 | 
			
		||||
     * @return Promise resolved when invalidated.
 | 
			
		||||
     */
 | 
			
		||||
    async invalidateModule(module: CoreCourseAnyModuleData, courseId: number): Promise<void> {
 | 
			
		||||
        // Invalidate forum data to recalculate unread message count badge.
 | 
			
		||||
        const promises: Promise<unknown>[] = [];
 | 
			
		||||
 | 
			
		||||
        promises.push(AddonModForum.instance.invalidateForumData(courseId));
 | 
			
		||||
        promises.push(CoreCourse.instance.invalidateModule(module.id));
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch a module.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module Module.
 | 
			
		||||
     * @param courseId Course ID the module belongs to.
 | 
			
		||||
     * @param single True if we're downloading a single module, false if we're downloading a whole section.
 | 
			
		||||
     * @param dirPath Path of the directory where to store all the content files.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    prefetch(module: CoreCourseAnyModuleData, courseId?: number, single?: boolean): Promise<void> {
 | 
			
		||||
        return this.prefetchPackage(module, courseId, this.prefetchForum.bind(this, module, courseId, single));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch a forum.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module The module object returned by WS.
 | 
			
		||||
     * @param courseId Course ID the module belongs to.
 | 
			
		||||
     * @param single True if we're downloading a single module, false if we're downloading a whole section.
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async prefetchForum(
 | 
			
		||||
        module: CoreCourseAnyModuleData,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        single: boolean,
 | 
			
		||||
        siteId: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const commonOptions = {
 | 
			
		||||
            readingStrategy: CoreSitesReadingStrategy.OnlyNetwork,
 | 
			
		||||
            siteId,
 | 
			
		||||
        };
 | 
			
		||||
        const modOptions = {
 | 
			
		||||
            cmId: module.id,
 | 
			
		||||
            ...commonOptions, // Include all common options.
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Get the forum data.
 | 
			
		||||
        const forum = await AddonModForum.instance.getForum(courseId, module.id, commonOptions);
 | 
			
		||||
        const promises: Promise<unknown>[] = [];
 | 
			
		||||
 | 
			
		||||
        // Prefetch the posts.
 | 
			
		||||
        promises.push(this.getPostsForPrefetch(forum, modOptions).then((posts) => {
 | 
			
		||||
            const promises: Promise<unknown>[] = [];
 | 
			
		||||
 | 
			
		||||
            const files = this.getIntroFilesFromInstance(module, forum).concat(this.getPostsFiles(posts));
 | 
			
		||||
            promises.push(CoreFilepool.instance.addFilesToQueue(siteId, files, this.component, module.id));
 | 
			
		||||
 | 
			
		||||
            // Prefetch groups data.
 | 
			
		||||
            promises.push(this.prefetchGroupsInfo(forum, courseId, !!forum.cancreatediscussions, siteId));
 | 
			
		||||
 | 
			
		||||
            // Prefetch avatars.
 | 
			
		||||
            promises.push(CoreUser.instance.prefetchUserAvatars(posts, 'userpictureurl', siteId));
 | 
			
		||||
 | 
			
		||||
            return Promise.all(promises);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        // Prefetch access information.
 | 
			
		||||
        promises.push(AddonModForum.instance.getAccessInformation(forum.id, modOptions));
 | 
			
		||||
 | 
			
		||||
        // Prefetch sort order preference.
 | 
			
		||||
        if (AddonModForum.instance.isDiscussionListSortingAvailable()) {
 | 
			
		||||
            promises.push(CoreUser.instance.getUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER, siteId));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await Promise.all(promises);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch groups info for a forum.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module The module object returned by WS.
 | 
			
		||||
     * @param courseI Course ID the module belongs to.
 | 
			
		||||
     * @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.
 | 
			
		||||
     */
 | 
			
		||||
    protected async prefetchGroupsInfo(
 | 
			
		||||
        forum: AddonModForumData,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        canCreateDiscussions: boolean,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        const options = {
 | 
			
		||||
            cmId: forum.cmid,
 | 
			
		||||
            siteId,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Check group mode.
 | 
			
		||||
        try {
 | 
			
		||||
            const mode = await CoreGroups.instance.getActivityGroupMode(forum.cmid, siteId);
 | 
			
		||||
 | 
			
		||||
            if (mode !== CoreGroupsProvider.SEPARATEGROUPS && mode !== CoreGroupsProvider.VISIBLEGROUPS) {
 | 
			
		||||
                // Activity doesn't use groups. Prefetch canAddDiscussionToAll to determine if user can pin/attach.
 | 
			
		||||
                await CoreUtils.instance.ignoreErrors(AddonModForum.instance.canAddDiscussionToAll(forum.id, options));
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Activity uses groups, prefetch allowed groups.
 | 
			
		||||
            const result = await CoreGroups.instance.getActivityAllowedGroups(forum.cmid, undefined, siteId);
 | 
			
		||||
            if (mode === CoreGroupsProvider.SEPARATEGROUPS) {
 | 
			
		||||
                // Groups are already filtered by WS. Prefetch canAddDiscussionToAll to determine if user can pin/attach.
 | 
			
		||||
                await CoreUtils.instance.ignoreErrors(AddonModForum.instance.canAddDiscussionToAll(forum.id, options));
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (canCreateDiscussions) {
 | 
			
		||||
                // Prefetch data to check the visible groups when creating discussions.
 | 
			
		||||
                const response = await CoreUtils.instance.ignoreErrors(
 | 
			
		||||
                    AddonModForum.instance.canAddDiscussionToAll(forum.id, options),
 | 
			
		||||
                    { status: false },
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                if (response.status) {
 | 
			
		||||
                    // User can post to all groups, nothing else to prefetch.
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // The user can't post to all groups, let's check which groups he can post to.
 | 
			
		||||
                await Promise.all(
 | 
			
		||||
                    result.groups.map(
 | 
			
		||||
                        async (group) => CoreUtils.instance.ignoreErrors(
 | 
			
		||||
                            AddonModForum.instance.canAddDiscussion(forum.id, group.id, options),
 | 
			
		||||
                        ),
 | 
			
		||||
                    ),
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // Ignore errors if cannot create discussions.
 | 
			
		||||
            if (canCreateDiscussions) {
 | 
			
		||||
                throw error;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sync a module.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module Module.
 | 
			
		||||
     * @param courseId Course ID the module belongs to
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async sync(
 | 
			
		||||
        module: CoreCourseAnyModuleData,
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        siteId?: string,
 | 
			
		||||
    ): Promise<AddonModForumSyncResult> {
 | 
			
		||||
        const promises: Promise<AddonModForumSyncResult>[] = [];
 | 
			
		||||
 | 
			
		||||
        promises.push(AddonModForumSync.instance.syncForumDiscussions(module.instance!, undefined, siteId));
 | 
			
		||||
        promises.push(AddonModForumSync.instance.syncForumReplies(module.instance!, undefined, siteId));
 | 
			
		||||
        promises.push(AddonModForumSync.instance.syncRatings(module.id, undefined, true, siteId));
 | 
			
		||||
 | 
			
		||||
        const results = await Promise.all(promises);
 | 
			
		||||
 | 
			
		||||
        return results.reduce(
 | 
			
		||||
            (a, b) => ({
 | 
			
		||||
                updated: a.updated || b.updated,
 | 
			
		||||
                warnings: (a.warnings || []).concat(b.warnings || []),
 | 
			
		||||
            }),
 | 
			
		||||
            {
 | 
			
		||||
                updated: false,
 | 
			
		||||
                warnings: [],
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class AddonModForumPrefetchHandler extends makeSingleton(AddonModForumPrefetchHandlerService) {}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by a forum sync.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModForumSyncResult = {
 | 
			
		||||
    warnings: string[]; // List of warnings.
 | 
			
		||||
    updated: boolean; // Whether some data was sent to the server or offline data was updated.
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										51
									
								
								src/addons/mod/forum/services/handlers/sync-cron.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/addons/mod/forum/services/handlers/sync-cron.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
// (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 { CoreCronHandler } from '@services/cron';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonModForumSync } from '../sync.service';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Synchronization cron handler.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonModForumSyncCronHandlerService implements CoreCronHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonModForumSyncCronHandler';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Execute the process.
 | 
			
		||||
     * Receives the ID of the site affected, undefined for all sites.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId ID of the site affected, undefined for all sites.
 | 
			
		||||
     * @param force Wether the execution is forced (manual sync).
 | 
			
		||||
     * @return Promise resolved when done, rejected if failure.
 | 
			
		||||
     */
 | 
			
		||||
    execute(siteId?: string, force?: boolean): Promise<void> {
 | 
			
		||||
        return AddonModForumSync.instance.syncAllForums(siteId, force);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the time between consecutive executions.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Time between consecutive executions (in ms).
 | 
			
		||||
     */
 | 
			
		||||
    getInterval(): number {
 | 
			
		||||
        return AddonModForumSync.instance.syncInterval;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class AddonModForumSyncCronHandler extends makeSingleton(AddonModForumSyncCronHandlerService) {}
 | 
			
		||||
@ -25,7 +25,7 @@ import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { makeSingleton, Translate } from '@singletons';
 | 
			
		||||
import { CoreArray } from '@singletons/array';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreEvents, CoreEventSiteData } from '@singletons/events';
 | 
			
		||||
import {
 | 
			
		||||
    AddonModForum,
 | 
			
		||||
    AddonModForumAddDiscussionPostWSOptionsObject,
 | 
			
		||||
@ -95,7 +95,7 @@ export class AddonModForumSyncProvider extends CoreSyncBaseProvider<AddonModForu
 | 
			
		||||
 | 
			
		||||
                if (result && result.updated) {
 | 
			
		||||
                    // Sync successful, send event.
 | 
			
		||||
                    CoreEvents.trigger(AddonModForumSyncProvider.AUTO_SYNCED, {
 | 
			
		||||
                    CoreEvents.trigger<AddonModForumAutoSyncData>(AddonModForumSyncProvider.AUTO_SYNCED, {
 | 
			
		||||
                        forumId: discussion.forumid,
 | 
			
		||||
                        userId: discussion.userid,
 | 
			
		||||
                        warnings: result.warnings,
 | 
			
		||||
@ -127,7 +127,7 @@ export class AddonModForumSyncProvider extends CoreSyncBaseProvider<AddonModForu
 | 
			
		||||
 | 
			
		||||
                if (result && result.updated) {
 | 
			
		||||
                    // Sync successful, send event.
 | 
			
		||||
                    CoreEvents.trigger(AddonModForumSyncProvider.AUTO_SYNCED, {
 | 
			
		||||
                    CoreEvents.trigger<AddonModForumAutoSyncData>(AddonModForumSyncProvider.AUTO_SYNCED, {
 | 
			
		||||
                        forumId: reply.forumid,
 | 
			
		||||
                        discussionId: reply.discussionid,
 | 
			
		||||
                        userId: reply.userid,
 | 
			
		||||
@ -619,3 +619,23 @@ export type AddonModForumSyncResult = {
 | 
			
		||||
    updated: boolean;
 | 
			
		||||
    warnings: string[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data passed to AUTO_SYNCED event.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModForumAutoSyncData = CoreEventSiteData & {
 | 
			
		||||
    forumId: number;
 | 
			
		||||
    userId: number;
 | 
			
		||||
    warnings: string[];
 | 
			
		||||
    discussionId?: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data passed to MANUAL_SYNCED event.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModForumManualSyncData = CoreEventSiteData & {
 | 
			
		||||
    forumId: number;
 | 
			
		||||
    userId: number;
 | 
			
		||||
    source: string;
 | 
			
		||||
    discussionId?: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -147,7 +147,10 @@ export abstract class CorePageItemsListManager<Item> {
 | 
			
		||||
        const params = this.getItemQueryParams(item);
 | 
			
		||||
        const pathPrefix = selectedItemPath ? selectedItemPath.split('/').fill('../').join('') : '';
 | 
			
		||||
 | 
			
		||||
        await CoreNavigator.instance.navigate(pathPrefix + itemPath, { params, reset: true });
 | 
			
		||||
        await CoreNavigator.instance.navigate(pathPrefix + itemPath, {
 | 
			
		||||
            params,
 | 
			
		||||
            reset: CoreScreen.instance.isTablet,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user