forked from CIT/Vmeda.Online
		
	MOBILE-3757 course: Update cached data after commpletion changed
This commit is contained in:
		
							parent
							
								
									61a908216d
								
							
						
					
					
						commit
						ac8cc189f7
					
				@ -1447,6 +1447,17 @@
 | 
			
		||||
  "core.course.askadmintosupport": "local_moodlemobileapp",
 | 
			
		||||
  "core.course.availablespace": "local_moodlemobileapp",
 | 
			
		||||
  "core.course.cannotdeletewhiledownloading": "local_moodlemobileapp",
 | 
			
		||||
  "core.course.completion_automatic:done": "course",
 | 
			
		||||
  "core.course.completion_automatic:failed": "course",
 | 
			
		||||
  "core.course.completion_automatic:todo": "course",
 | 
			
		||||
  "core.course.completion_manual:aria:done": "course",
 | 
			
		||||
  "core.course.completion_manual:aria:markdone": "course",
 | 
			
		||||
  "core.course.completion_manual:markdone": "course",
 | 
			
		||||
  "core.course.completion_setby:auto:done": "course",
 | 
			
		||||
  "core.course.completion_setby:auto:todo": "course",
 | 
			
		||||
  "core.course.completion_setby:manual:done": "course",
 | 
			
		||||
  "core.course.completion_setby:manual:markdone": "course",
 | 
			
		||||
  "core.course.completionrequirements": "course",
 | 
			
		||||
  "core.course.confirmdeletemodulefiles": "local_moodlemobileapp",
 | 
			
		||||
  "core.course.confirmdeletestoreddata": "local_moodlemobileapp",
 | 
			
		||||
  "core.course.confirmdownload": "local_moodlemobileapp",
 | 
			
		||||
 | 
			
		||||
@ -79,6 +79,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
 | 
			
		||||
    protected currentStatus?: string; // The current status of the module. Only if setStatusListener is called.
 | 
			
		||||
    protected completionObserver?: CoreEventObserver;
 | 
			
		||||
    protected logger: CoreLogger;
 | 
			
		||||
    protected debouncedUpdateModule?: () => void; // Update the module after a certain time.
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        @Optional() @Inject('') loggerName: string = 'CoreCourseModuleMainResourceComponent',
 | 
			
		||||
@ -103,9 +104,13 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
 | 
			
		||||
                if (data && data.cmId == this.module.id) {
 | 
			
		||||
                    await CoreCourse.invalidateModule(this.module.id);
 | 
			
		||||
 | 
			
		||||
                    this.module = await CoreCourse.getModule(this.module.id, this.courseId);
 | 
			
		||||
                    this.fetchModule();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            this.debouncedUpdateModule = CoreUtils.debounce(() => {
 | 
			
		||||
                this.fetchModule();
 | 
			
		||||
            }, 10000);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.blog = await AddonBlog.isPluginEnabled();
 | 
			
		||||
@ -160,7 +165,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
 | 
			
		||||
            ]));
 | 
			
		||||
 | 
			
		||||
            if (this.showCompletion) {
 | 
			
		||||
                this.module = await CoreCourse.getModule(this.module.id, this.courseId);
 | 
			
		||||
                this.fetchModule();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await this.loadContent(true);
 | 
			
		||||
@ -403,8 +408,23 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async onCompletionChange(): Promise<void> {
 | 
			
		||||
        // Nothing to do.
 | 
			
		||||
        return;
 | 
			
		||||
        // Update the module data after a while.
 | 
			
		||||
        this.debouncedUpdateModule?.();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch module.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    protected async fetchModule(): Promise<void> {
 | 
			
		||||
        const module = await CoreCourse.getModule(this.module.id, this.courseId);
 | 
			
		||||
 | 
			
		||||
        CoreCourseHelper.calculateModuleCompletionData(module, this.courseId);
 | 
			
		||||
 | 
			
		||||
        await CoreCourseHelper.loadModuleOfflineCompletion(this.courseId, module);
 | 
			
		||||
 | 
			
		||||
        this.module = module;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@ import { Component, Input } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreCourseModuleCompletionBaseComponent } from '@features/course/classes/module-completion';
 | 
			
		||||
import { CoreCourseModuleWSRuleDetails, CoreCourseProvider } from '@features/course/services/course';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -43,13 +44,13 @@ export class CoreCourseModuleCompletionComponent extends CoreCourseModuleComplet
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    protected calculateData(): void {
 | 
			
		||||
    protected async calculateData(): Promise<void> {
 | 
			
		||||
        if (!this.completion?.details) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Format rules.
 | 
			
		||||
        this.details = this.completion.details.map((rule: CompletionRule) => {
 | 
			
		||||
        this.details = await Promise.all(this.completion.details.map(async (rule: CompletionRule) => {
 | 
			
		||||
            rule.statuscomplete = rule.rulevalue.status == CoreCourseProvider.COMPLETION_COMPLETE ||
 | 
			
		||||
                    rule.rulevalue.status == CoreCourseProvider.COMPLETION_COMPLETE_PASS;
 | 
			
		||||
            rule.statuscompletefail = rule.rulevalue.status == CoreCourseProvider.COMPLETION_COMPLETE_FAIL;
 | 
			
		||||
@ -57,10 +58,12 @@ export class CoreCourseModuleCompletionComponent extends CoreCourseModuleComplet
 | 
			
		||||
            rule.accessibleDescription = null;
 | 
			
		||||
 | 
			
		||||
            if (this.completion!.overrideby) {
 | 
			
		||||
                const fullName = await CoreUser.getUserFullNameWithDefault(this.completion!.overrideby, this.completion!.courseId);
 | 
			
		||||
 | 
			
		||||
                const setByData = {
 | 
			
		||||
                    $a: {
 | 
			
		||||
                        condition: rule.rulevalue.description,
 | 
			
		||||
                        setby: this.completion!.overrideby,
 | 
			
		||||
                        setby: fullName,
 | 
			
		||||
                    },
 | 
			
		||||
                };
 | 
			
		||||
                const overrideStatus = rule.statuscomplete ? 'done' : 'todo';
 | 
			
		||||
@ -69,7 +72,7 @@ export class CoreCourseModuleCompletionComponent extends CoreCourseModuleComplet
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return rule;
 | 
			
		||||
        });
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,7 @@
 | 
			
		||||
import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChange } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
import { CoreCourseHelper, CoreCourseModuleCompletionData } from '@features/course/services/course-helper';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
import { Translate } from '@singletons';
 | 
			
		||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
 | 
			
		||||
 | 
			
		||||
@ -41,10 +42,13 @@ export class CoreCourseModuleManualCompletionComponent implements OnInit, OnChan
 | 
			
		||||
     */
 | 
			
		||||
    ngOnInit(): void {
 | 
			
		||||
        this.manualChangedObserver = CoreEvents.on(CoreEvents.MANUAL_COMPLETION_CHANGED, (data) => {
 | 
			
		||||
            if (this.completion && this.completion.cmid == data.completion.cmid) {
 | 
			
		||||
                this.completion = data.completion;
 | 
			
		||||
                this.calculateData();
 | 
			
		||||
            if (!this.completion || this.completion.cmid != data.completion.cmid) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.completion = data.completion;
 | 
			
		||||
            this.calculateData();
 | 
			
		||||
            this.completionChanged.emit(this.completion);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -60,17 +64,19 @@ export class CoreCourseModuleManualCompletionComponent implements OnInit, OnChan
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    protected calculateData(): void {
 | 
			
		||||
        if (!this.completion?.isautomatic) {
 | 
			
		||||
    protected async calculateData(): Promise<void> {
 | 
			
		||||
        if (!this.completion || this.completion.isautomatic) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Set an accessible description for manual completions with overridden completion state.
 | 
			
		||||
        if (this.completion.overrideby) {
 | 
			
		||||
            const fullName = await CoreUser.getUserFullNameWithDefault(this.completion.overrideby, this.completion.courseId);
 | 
			
		||||
 | 
			
		||||
            const setByData = {
 | 
			
		||||
                $a: {
 | 
			
		||||
                    activityname: this.moduleName,
 | 
			
		||||
                    setby: this.completion.overrideby,
 | 
			
		||||
                    setby: fullName,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            const setByLangKey = this.completion.state ? 'completion_setby:manual:done' : 'completion_setby:manual:markdone';
 | 
			
		||||
@ -93,10 +99,7 @@ export class CoreCourseModuleManualCompletionComponent implements OnInit, OnChan
 | 
			
		||||
 | 
			
		||||
        await CoreCourseHelper.changeManualCompletion(this.completion, event);
 | 
			
		||||
 | 
			
		||||
        this.calculateData();
 | 
			
		||||
 | 
			
		||||
        CoreEvents.trigger(CoreEvents.MANUAL_COMPLETION_CHANGED, { completion: this.completion });
 | 
			
		||||
        this.completionChanged.emit(this.completion);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -81,6 +81,8 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
 | 
			
		||||
    protected courseStatusObserver?: CoreEventObserver;
 | 
			
		||||
    protected syncObserver?: CoreEventObserver;
 | 
			
		||||
    protected isDestroyed = false;
 | 
			
		||||
    protected modulesHaveCompletion = false;
 | 
			
		||||
    protected debouncedUpdateCachedCompletion?: () => void; // Update the cached completion after a certain time.
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Component being initialized.
 | 
			
		||||
@ -104,6 +106,21 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
 | 
			
		||||
            CoreCourseFormatDelegate.displayEnableDownload(this.course);
 | 
			
		||||
        this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite();
 | 
			
		||||
 | 
			
		||||
        this.debouncedUpdateCachedCompletion = CoreUtils.debounce(() => {
 | 
			
		||||
            if (this.modulesHaveCompletion) {
 | 
			
		||||
                CoreUtils.ignoreErrors(CoreCourse.getSections(this.course.id, false, true));
 | 
			
		||||
            } else {
 | 
			
		||||
                CoreUtils.ignoreErrors(CoreCourse.getActivitiesCompletionStatus(
 | 
			
		||||
                    this.course.id,
 | 
			
		||||
                    undefined,
 | 
			
		||||
                    undefined,
 | 
			
		||||
                    false,
 | 
			
		||||
                    false,
 | 
			
		||||
                    false,
 | 
			
		||||
                ));
 | 
			
		||||
            }
 | 
			
		||||
        }, 30000);
 | 
			
		||||
 | 
			
		||||
        this.initListeners();
 | 
			
		||||
 | 
			
		||||
        await this.loadData(false, true);
 | 
			
		||||
@ -254,6 +271,8 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
            if (sectionWithModules && typeof sectionWithModules.modules[0].completion != 'undefined') {
 | 
			
		||||
                // The module already has completion (3.6 onwards). Load the offline completion.
 | 
			
		||||
                this.modulesHaveCompletion = true;
 | 
			
		||||
 | 
			
		||||
                await CoreUtils.ignoreErrors(CoreCourseHelper.loadOfflineCompletion(this.course.id, sections));
 | 
			
		||||
            } else {
 | 
			
		||||
                const fetchedData = await CoreUtils.ignoreErrors(
 | 
			
		||||
@ -356,6 +375,8 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
 | 
			
		||||
            // Invalidate the completion.
 | 
			
		||||
            await CoreUtils.ignoreErrors(CoreCourse.invalidateSections(this.course.id));
 | 
			
		||||
 | 
			
		||||
            this.debouncedUpdateCachedCompletion?.();
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -195,11 +195,8 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
                    forCoursePage,
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                if (module.completiondata && module.completion && module.completion > 0) {
 | 
			
		||||
                    module.completiondata.courseId = courseId;
 | 
			
		||||
                    module.completiondata.courseName = courseName;
 | 
			
		||||
                    module.completiondata.tracking = module.completion;
 | 
			
		||||
                    module.completiondata.cmid = module.id;
 | 
			
		||||
                if (module.completiondata) {
 | 
			
		||||
                    this.calculateModuleCompletionData(module, courseId, courseName);
 | 
			
		||||
                } else if (completionStatus && typeof completionStatus[module.id] != 'undefined') {
 | 
			
		||||
                    // Should not happen on > 3.6. Check if activity has completions and if it's marked.
 | 
			
		||||
                    const activityStatus = completionStatus[module.id];
 | 
			
		||||
@ -224,6 +221,24 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
        return { hasContent, sections: formattedSections };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate completion data of a module.
 | 
			
		||||
     *
 | 
			
		||||
     * @param module Module.
 | 
			
		||||
     * @param courseId Course ID of the module.
 | 
			
		||||
     * @param courseName Course name.
 | 
			
		||||
     */
 | 
			
		||||
    calculateModuleCompletionData(module: CoreCourseModule, courseId: number, courseName?: string): void {
 | 
			
		||||
        if (!module.completiondata || !module.completion) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        module.completiondata.courseId = courseId;
 | 
			
		||||
        module.completiondata.courseName = courseName;
 | 
			
		||||
        module.completiondata.tracking = module.completion;
 | 
			
		||||
        module.completiondata.cmid = module.id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate the status of a section.
 | 
			
		||||
     *
 | 
			
		||||
@ -1177,6 +1192,31 @@ export class CoreCourseHelperProvider {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Load offline completion for a certain module.
 | 
			
		||||
     * This should be used in 3.6 sites or higher, where the course contents already include the completion.
 | 
			
		||||
     *
 | 
			
		||||
     * @param courseId The course to get the completion.
 | 
			
		||||
     * @param mmodule The module.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async loadModuleOfflineCompletion(courseId: number, module: CoreCourseModule, siteId?: string): Promise<void> {
 | 
			
		||||
        if (!module.completiondata) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const offlineCompletions = await CoreCourseOffline.getCourseManualCompletions(courseId, siteId);
 | 
			
		||||
 | 
			
		||||
        const offlineCompletion = offlineCompletions.find(completion => completion.cmid == module.id);
 | 
			
		||||
 | 
			
		||||
        if (offlineCompletion && offlineCompletion.timecompleted >= module.completiondata.timecompleted * 1000) {
 | 
			
		||||
            // The module has offline completion. Load it.
 | 
			
		||||
            module.completiondata.state = offlineCompletion.completed;
 | 
			
		||||
            module.completiondata.offline = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prefetch all the courses in the array.
 | 
			
		||||
     *
 | 
			
		||||
 | 
			
		||||
@ -23,5 +23,6 @@
 | 
			
		||||
    "sendemail": "Email",
 | 
			
		||||
    "student": "Student",
 | 
			
		||||
    "teacher": "Non-editing teacher",
 | 
			
		||||
    "userwithid": "User with ID {{id}}",
 | 
			
		||||
    "webpage": "Web page"
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreUserOffline } from './user-offline';
 | 
			
		||||
import { CoreLogger } from '@singletons/logger';
 | 
			
		||||
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { makeSingleton, Translate } from '@singletons';
 | 
			
		||||
import { CoreEvents } from '@singletons/events';
 | 
			
		||||
import { CoreStatusWithWarningsWSResponse, CoreWSExternalWarning } from '@services/ws';
 | 
			
		||||
import { CoreError } from '@classes/errors/error';
 | 
			
		||||
@ -305,6 +305,25 @@ export class CoreUserProvider {
 | 
			
		||||
        return site.getDb().getRecord(USERS_TABLE_NAME, { id: userId });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get a user fullname, using a default text if user not found.
 | 
			
		||||
     *
 | 
			
		||||
     * @param userId User ID.
 | 
			
		||||
     * @param courseId Course ID.
 | 
			
		||||
     * @param siteId Site ID.
 | 
			
		||||
     * @return Promise resolved with user name.
 | 
			
		||||
     */
 | 
			
		||||
    async getUserFullNameWithDefault(userId: number, courseId?: number, siteId?: string): Promise<string> {
 | 
			
		||||
        try {
 | 
			
		||||
            const user = await CoreUser.getProfile(userId, courseId, true, siteId);
 | 
			
		||||
 | 
			
		||||
            return user.fullname;
 | 
			
		||||
 | 
			
		||||
        } catch {
 | 
			
		||||
            return Translate.instant('core.user.userwithid', { id: userId });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get user profile from WS.
 | 
			
		||||
     *
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user