diff --git a/src/addon/blog/providers/blog.ts b/src/addon/blog/providers/blog.ts index adc15b1f2..e5387cb65 100644 --- a/src/addon/blog/providers/blog.ts +++ b/src/addon/blog/providers/blog.ts @@ -16,6 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; +import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; /** * Service to handle blog entries. @@ -27,7 +28,8 @@ export class AddonBlogProvider { protected ROOT_CACHE_KEY = 'addonBlog:'; protected logger; - constructor(logger: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, protected utils: CoreUtilsProvider) { + constructor(logger: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, protected utils: CoreUtilsProvider, + protected pushNotificationsProvider: CorePushNotificationsProvider) { this.logger = logger.getInstance('AddonBlogProvider'); } @@ -102,6 +104,8 @@ export class AddonBlogProvider { * @return {Promise} Promise to be resolved when done. */ logView(filter: any = {}, siteId?: string): Promise { + this.pushNotificationsProvider.logViewListEvent('blog', 'core_blog_view_entries', filter, siteId); + return this.sitesProvider.getSite(siteId).then((site) => { const data = { filters: this.utils.objectToArrayOfObjects(filter, 'name', 'value') diff --git a/src/addon/competency/pages/competency/competency.ts b/src/addon/competency/pages/competency/competency.ts index 9552e75a6..dc7e76c9d 100644 --- a/src/addon/competency/pages/competency/competency.ts +++ b/src/addon/competency/pages/competency/competency.ts @@ -55,13 +55,16 @@ export class AddonCompetencyCompetencyPage { */ ionViewDidLoad(): void { this.fetchCompetency().then(() => { + const name = this.competency && this.competency.competency && this.competency.competency.competency && + this.competency.competency.competency.shortname; + if (this.planId) { - this.competencyProvider.logCompetencyInPlanView(this.planId, this.competencyId, this.planStatus, this.userId) - .catch(() => { + this.competencyProvider.logCompetencyInPlanView(this.planId, this.competencyId, this.planStatus, name, + this.userId).catch(() => { // Ignore errors. }); } else { - this.competencyProvider.logCompetencyInCourseView(this.courseId, this.competencyId, this.userId).catch(() => { + this.competencyProvider.logCompetencyInCourseView(this.courseId, this.competencyId, name, this.userId).catch(() => { // Ignore errors. }); } diff --git a/src/addon/competency/pages/competencysummary/competencysummary.ts b/src/addon/competency/pages/competencysummary/competencysummary.ts index 3045f44f3..db4c7a32d 100644 --- a/src/addon/competency/pages/competencysummary/competencysummary.ts +++ b/src/addon/competency/pages/competencysummary/competencysummary.ts @@ -41,7 +41,10 @@ export class AddonCompetencyCompetencySummaryPage { */ ionViewDidLoad(): void { this.fetchCompetency().then(() => { - this.competencyProvider.logCompetencyView(this.competencyId).catch(() => { + const name = this.competency.competency && this.competency.competency.competency && + this.competency.competency.competency.shortname; + + this.competencyProvider.logCompetencyView(this.competencyId, name).catch(() => { // Ignore errors. }); }).finally(() => { diff --git a/src/addon/competency/providers/competency.ts b/src/addon/competency/providers/competency.ts index 455c2fd93..7786b75a6 100644 --- a/src/addon/competency/providers/competency.ts +++ b/src/addon/competency/providers/competency.ts @@ -15,6 +15,7 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider } from '@providers/sites'; +import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; /** * Service to handle caompetency learning plans. @@ -38,7 +39,8 @@ export class AddonCompetencyProvider { protected logger; - constructor(loggerProvider: CoreLoggerProvider, private sitesProvider: CoreSitesProvider) { + constructor(loggerProvider: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, + protected pushNotificationsProvider: CorePushNotificationsProvider) { this.logger = loggerProvider.getInstance('AddonCompetencyProvider'); } @@ -471,13 +473,15 @@ export class AddonCompetencyProvider { * @param {number} planId ID of the plan. * @param {number} competencyId ID of the competency. * @param {number} planStatus Current plan Status to decide what action should be logged. + * @param {string} [name] Name of the competency. * @param {number} [userId] User ID. If not defined, current user. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logCompetencyInPlanView(planId: number, competencyId: number, planStatus: number, userId?: number, siteId?: string) - : Promise { + logCompetencyInPlanView(planId: number, competencyId: number, planStatus: number, name?: string, userId?: number, + siteId?: string): Promise { if (planId && competencyId) { + return this.sitesProvider.getSite(siteId).then((site) => { userId = userId || site.getUserId(); @@ -488,13 +492,17 @@ export class AddonCompetencyProvider { }, preSets = { typeExpected: 'boolean' - }; + }, + wsName = planStatus == AddonCompetencyProvider.STATUS_COMPLETE ? + 'core_competency_user_competency_plan_viewed' : 'core_competency_user_competency_viewed_in_plan'; - if (planStatus == AddonCompetencyProvider.STATUS_COMPLETE) { - return site.write('core_competency_user_competency_plan_viewed', params, preSets); - } else { - return site.write('core_competency_user_competency_viewed_in_plan', params, preSets); - } + this.pushNotificationsProvider.logViewEvent(competencyId, name, 'competency', wsName, { + planid: planId, + planstatus: planStatus, + userid: userId + }, siteId); + + return site.write(wsName, params, preSets); }); } @@ -506,11 +514,14 @@ export class AddonCompetencyProvider { * * @param {number} courseId ID of the course. * @param {number} competencyId ID of the competency. + * @param {string} [name] Name of the competency. * @param {number} [userId] User ID. If not defined, current user. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logCompetencyInCourseView(courseId: number, competencyId: number, userId?: number, siteId?: string): Promise { + logCompetencyInCourseView(courseId: number, competencyId: number, name?: string, userId?: number, siteId?: string) + : Promise { + if (courseId && competencyId) { return this.sitesProvider.getSite(siteId).then((site) => { userId = userId || site.getUserId(); @@ -523,8 +534,14 @@ export class AddonCompetencyProvider { const preSets = { typeExpected: 'boolean' }; + const wsName = 'core_competency_user_competency_viewed_in_course'; - return site.write('core_competency_user_competency_viewed_in_course', params, preSets); + this.pushNotificationsProvider.logViewEvent(competencyId, name, 'competency', wsName, { + courseid: courseId, + userid: userId + }, siteId); + + return site.write(wsName, params, preSets); }); } @@ -535,10 +552,11 @@ export class AddonCompetencyProvider { * Report the competency as being viewed. * * @param {number} competencyId ID of the competency. + * @param {string} [name] Name of the competency. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logCompetencyView(competencyId: number, siteId?: string): Promise { + logCompetencyView(competencyId: number, name?: string, siteId?: string): Promise { if (competencyId) { return this.sitesProvider.getSite(siteId).then((site) => { const params = { @@ -547,6 +565,9 @@ export class AddonCompetencyProvider { const preSets = { typeExpected: 'boolean' }; + const wsName = 'core_competency_competency_viewed'; + + this.pushNotificationsProvider.logViewEvent(competencyId, name, 'competency', wsName, {}, siteId); return site.write('core_competency_competency_viewed', params, preSets); }); diff --git a/src/addon/mod/assign/components/index/index.ts b/src/addon/mod/assign/components/index/index.ts index 7cd42a015..013c6b378 100644 --- a/src/addon/mod/assign/components/index/index.ts +++ b/src/addon/mod/assign/components/index/index.ts @@ -80,7 +80,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo this.userId = this.sitesProvider.getCurrentSiteUserId(); this.loadContent(false, true).then(() => { - this.assignProvider.logView(this.assign.id).then(() => { + this.assignProvider.logView(this.assign.id, this.assign.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch(() => { // Ignore errors. @@ -88,12 +88,12 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo if (this.canViewAllSubmissions) { // User can see all submissions, log grading view. - this.assignProvider.logGradingView(this.assign.id).catch(() => { + this.assignProvider.logGradingView(this.assign.id, this.assign.name).catch(() => { // Ignore errors. }); } else if (this.canViewOwnSubmission) { // User can only see their own submission, log view the user submission. - this.assignProvider.logSubmissionView(this.assign.id).catch(() => { + this.assignProvider.logSubmissionView(this.assign.id, this.assign.name).catch(() => { // Ignore errors. }); } diff --git a/src/addon/mod/assign/providers/assign.ts b/src/addon/mod/assign/providers/assign.ts index 461548f7e..1b785e8b5 100644 --- a/src/addon/mod/assign/providers/assign.ts +++ b/src/addon/mod/assign/providers/assign.ts @@ -911,16 +911,19 @@ export class AddonModAssignProvider { * Report an assignment submission as being viewed. * * @param {number} assignId Assignment ID. + * @param {string} [name] Name of the assign. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logSubmissionView(assignId: number, siteId?: string): Promise { + logSubmissionView(assignId: number, name?: string, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { const params = { assignid: assignId }; - return site.write('mod_assign_view_submission_status', params); + return this.logHelper.logSingle('mod_assign_view_submission_status', params, AddonModAssignProvider.COMPONENT, + assignId, name, 'assign', {}, siteId); }); } @@ -928,30 +931,34 @@ export class AddonModAssignProvider { * Report an assignment grading table is being viewed. * * @param {number} assignId Assignment ID. + * @param {string} [name] Name of the assign. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logGradingView(assignId: number, siteId?: string): Promise { + logGradingView(assignId: number, name?: string, siteId?: string): Promise { const params = { assignid: assignId }; - return this.logHelper.log('mod_assign_view_grading_table', params, AddonModAssignProvider.COMPONENT, assignId, siteId); + return this.logHelper.logSingle('mod_assign_view_grading_table', params, AddonModAssignProvider.COMPONENT, assignId, + name, 'assign', {}, siteId); } /** * Report an assign as being viewed. * * @param {number} assignId Assignment ID. + * @param {string} [name] Name of the assign. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(assignId: number, siteId?: string): Promise { + logView(assignId: number, name?: string, siteId?: string): Promise { const params = { assignid: assignId }; - return this.logHelper.log('mod_assign_view_assign', params, AddonModAssignProvider.COMPONENT, assignId, siteId); + return this.logHelper.logSingle('mod_assign_view_assign', params, AddonModAssignProvider.COMPONENT, assignId, name, + 'assign', {}, siteId); } /** diff --git a/src/addon/mod/book/components/index/index.ts b/src/addon/mod/book/components/index/index.ts index 75ad3beb1..1dd820508 100644 --- a/src/addon/mod/book/components/index/index.ts +++ b/src/addon/mod/book/components/index/index.ts @@ -184,7 +184,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp this.nextChapter = this.bookProvider.getNextChapter(this.chapters, chapterId); // Chapter loaded, log view. We don't return the promise because we don't want to block the user for this. - this.bookProvider.logView(this.module.instance, chapterId).then(() => { + this.bookProvider.logView(this.module.instance, chapterId, this.module.name).then(() => { // Module is completed when last chapter is viewed, so we only check completion if the last is reached. if (!this.nextChapter) { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); diff --git a/src/addon/mod/book/providers/book.ts b/src/addon/mod/book/providers/book.ts index e322284c6..4cb37834b 100644 --- a/src/addon/mod/book/providers/book.ts +++ b/src/addon/mod/book/providers/book.ts @@ -380,15 +380,17 @@ export class AddonModBookProvider { * * @param {number} id Module ID. * @param {string} chapterId Chapter ID. + * @param {string} [name] Name of the book. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, chapterId: string, siteId?: string): Promise { + logView(id: number, chapterId: string, name?: string, siteId?: string): Promise { const params = { bookid: id, chapterid: chapterId }; - return this.logHelper.log('mod_book_view_book', params, AddonModBookProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_book_view_book', params, AddonModBookProvider.COMPONENT, id, name, 'book', + {chapterid: chapterId}, siteId); } } diff --git a/src/addon/mod/chat/components/index/index.ts b/src/addon/mod/chat/components/index/index.ts index a04cf11d0..585c6f4e8 100644 --- a/src/addon/mod/chat/components/index/index.ts +++ b/src/addon/mod/chat/components/index/index.ts @@ -47,7 +47,7 @@ export class AddonModChatIndexComponent extends CoreCourseModuleMainActivityComp super.ngOnInit(); this.loadContent().then(() => { - this.chatProvider.logView(this.chat.id).then(() => { + this.chatProvider.logView(this.chat.id, this.chat.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch(() => { // Ignore errors. diff --git a/src/addon/mod/chat/providers/chat.ts b/src/addon/mod/chat/providers/chat.ts index f12107dc4..ed86a56d5 100644 --- a/src/addon/mod/chat/providers/chat.ts +++ b/src/addon/mod/chat/providers/chat.ts @@ -87,15 +87,16 @@ export class AddonModChatProvider { * Report a chat as being viewed. * * @param {number} id Chat instance ID. + * @param {string} [name] Name of the chat. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params = { chatid: id }; - return this.logHelper.log('mod_chat_view_chat', params, AddonModChatProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_chat_view_chat', params, AddonModChatProvider.COMPONENT, id, name, 'chat', {}, siteId); } /** diff --git a/src/addon/mod/choice/components/index/index.ts b/src/addon/mod/choice/components/index/index.ts index fb2243153..b543e4013 100644 --- a/src/addon/mod/choice/components/index/index.ts +++ b/src/addon/mod/choice/components/index/index.ts @@ -67,7 +67,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo if (!this.choice) { return; } - this.choiceProvider.logView(this.choice.id).then(() => { + this.choiceProvider.logView(this.choice.id, this.choice.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch((error) => { // Ignore errors. diff --git a/src/addon/mod/choice/providers/choice.ts b/src/addon/mod/choice/providers/choice.ts index b94e92b9c..7d1dd0e00 100644 --- a/src/addon/mod/choice/providers/choice.ts +++ b/src/addon/mod/choice/providers/choice.ts @@ -371,15 +371,17 @@ export class AddonModChoiceProvider { * Report the choice as being viewed. * * @param {string} id Choice ID. + * @param {string} [name] Name of the choice. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params = { choiceid: id }; - return this.logHelper.log('mod_choice_view_choice', params, AddonModChoiceProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_choice_view_choice', params, AddonModChoiceProvider.COMPONENT, id, name, 'choice', + {}, siteId); } /** diff --git a/src/addon/mod/data/components/index/index.ts b/src/addon/mod/data/components/index/index.ts index dcae5876c..37a806f43 100644 --- a/src/addon/mod/data/components/index/index.ts +++ b/src/addon/mod/data/components/index/index.ts @@ -129,7 +129,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp return; } - this.dataProvider.logView(this.data.id).then(() => { + this.dataProvider.logView(this.data.id, this.data.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch(() => { // Ignore errors. diff --git a/src/addon/mod/data/providers/data.ts b/src/addon/mod/data/providers/data.ts index f79ab69a2..47f8fc64d 100644 --- a/src/addon/mod/data/providers/data.ts +++ b/src/addon/mod/data/providers/data.ts @@ -928,15 +928,17 @@ export class AddonModDataProvider { * Report the database as being viewed. * * @param {number} id Module ID. + * @param {string} [name] Name of the data. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params = { databaseid: id }; - return this.logHelper.log('mod_data_view_database', params, AddonModDataProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_data_view_database', params, AddonModDataProvider.COMPONENT, id, name, 'data', {}, + siteId); } /** diff --git a/src/addon/mod/feedback/components/index/index.ts b/src/addon/mod/feedback/components/index/index.ts index 55ffc4d21..953a8c8bd 100644 --- a/src/addon/mod/feedback/components/index/index.ts +++ b/src/addon/mod/feedback/components/index/index.ts @@ -113,7 +113,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity super.ngOnInit(); this.loadContent(false, true).then(() => { - this.feedbackProvider.logView(this.feedback.id).catch(() => { + this.feedbackProvider.logView(this.feedback.id, this.feedback.name).catch(() => { // Ignore errors. }); }).finally(() => { diff --git a/src/addon/mod/feedback/pages/form/form.ts b/src/addon/mod/feedback/pages/form/form.ts index 82e24a29b..65a21bec8 100644 --- a/src/addon/mod/feedback/pages/form/form.ts +++ b/src/addon/mod/feedback/pages/form/form.ts @@ -95,7 +95,7 @@ export class AddonModFeedbackFormPage implements OnDestroy { */ ionViewDidLoad(): void { this.fetchData().then(() => { - this.feedbackProvider.logView(this.feedback.id, true).then(() => { + this.feedbackProvider.logView(this.feedback.id, this.feedback.name, true).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch(() => { // Ignore errors. diff --git a/src/addon/mod/feedback/providers/feedback.ts b/src/addon/mod/feedback/providers/feedback.ts index 14976deb3..23327ca75 100644 --- a/src/addon/mod/feedback/providers/feedback.ts +++ b/src/addon/mod/feedback/providers/feedback.ts @@ -1124,17 +1124,19 @@ export class AddonModFeedbackProvider { * Report the feedback as being viewed. * * @param {number} id Module ID. + * @param {string} [name] Name of the feedback. * @param {boolean} [formViewed=false] True if form was viewed. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, formViewed: boolean = false, siteId?: string): Promise { + logView(id: number, name?: string, formViewed: boolean = false, siteId?: string): Promise { const params = { feedbackid: id, moduleviewed: formViewed ? 1 : 0 }; - return this.logHelper.log('mod_feedback_view_feedback', params, AddonModFeedbackProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_feedback_view_feedback', params, AddonModFeedbackProvider.COMPONENT, id, name, + 'feedback', {moduleviewed: params.moduleviewed}, siteId); } /** diff --git a/src/addon/mod/folder/components/index/index.ts b/src/addon/mod/folder/components/index/index.ts index 6b735e53d..d8c296b00 100644 --- a/src/addon/mod/folder/components/index/index.ts +++ b/src/addon/mod/folder/components/index/index.ts @@ -55,7 +55,7 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo this.refreshIcon = 'refresh'; } else { this.loadContent().then(() => { - this.folderProvider.logView(this.module.instance).then(() => { + this.folderProvider.logView(this.module.instance, this.module.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch(() => { // Ignore errors. diff --git a/src/addon/mod/folder/providers/folder.ts b/src/addon/mod/folder/providers/folder.ts index f26369af8..eff8af19c 100644 --- a/src/addon/mod/folder/providers/folder.ts +++ b/src/addon/mod/folder/providers/folder.ts @@ -133,14 +133,16 @@ export class AddonModFolderProvider { * Report a folder as being viewed. * * @param {number} id Module ID. + * @param {string} [name] Name of the folder. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params = { folderid: id }; - return this.logHelper.log('mod_folder_view_folder', params, AddonModFolderProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_folder_view_folder', params, AddonModFolderProvider.COMPONENT, id, name, 'folder', + {}, siteId); } } diff --git a/src/addon/mod/forum/components/index/index.ts b/src/addon/mod/forum/components/index/index.ts index 6b8a5994c..2911bd925 100644 --- a/src/addon/mod/forum/components/index/index.ts +++ b/src/addon/mod/forum/components/index/index.ts @@ -152,7 +152,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom } } - this.forumProvider.logView(this.forum.id).then(() => { + this.forumProvider.logView(this.forum.id, this.forum.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch((error) => { // Ignore errors. diff --git a/src/addon/mod/forum/pages/discussion/discussion.ts b/src/addon/mod/forum/pages/discussion/discussion.ts index 3aee91f1b..3f4405bb9 100644 --- a/src/addon/mod/forum/pages/discussion/discussion.ts +++ b/src/addon/mod/forum/pages/discussion/discussion.ts @@ -384,7 +384,7 @@ export class AddonModForumDiscussionPage implements OnDestroy { if (forceMarkAsRead || (hasUnreadPosts && this.trackPosts)) { // // Add log in Moodle and mark unread posts as readed. - this.forumProvider.logDiscussionView(this.discussionId, this.forumId || -1).catch(() => { + this.forumProvider.logDiscussionView(this.discussionId, this.forumId || -1, this.forum.name).catch(() => { // Ignore errors. }).finally(() => { // Trigger mark read posts. diff --git a/src/addon/mod/forum/providers/forum.ts b/src/addon/mod/forum/providers/forum.ts index 0f6683e11..d7cb51411 100644 --- a/src/addon/mod/forum/providers/forum.ts +++ b/src/addon/mod/forum/providers/forum.ts @@ -771,15 +771,17 @@ export class AddonModForumProvider { * Report a forum as being viewed. * * @param {number} id Module ID. + * @param {string} [name] Name of the forum. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params = { forumid: id }; - return this.logHelper.log('mod_forum_view_forum', params, AddonModForumProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_forum_view_forum', params, AddonModForumProvider.COMPONENT, id, name, 'forum', {}, + siteId); } /** @@ -787,15 +789,17 @@ export class AddonModForumProvider { * * @param {number} id Discussion ID. * @param {number} forumId Forum ID. + * @param {string} [name] Name of the forum. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logDiscussionView(id: number, forumId: number, siteId?: string): Promise { + logDiscussionView(id: number, forumId: number, name?: string, siteId?: string): Promise { const params = { discussionid: id }; - return this.logHelper.log('mod_forum_view_forum_discussion', params, AddonModForumProvider.COMPONENT, forumId, siteId); + return this.logHelper.logSingle('mod_forum_view_forum_discussion', params, AddonModForumProvider.COMPONENT, forumId, name, + 'forum', params, siteId); } /** diff --git a/src/addon/mod/glossary/components/index/index.ts b/src/addon/mod/glossary/components/index/index.ts index 1f473e081..558eac531 100644 --- a/src/addon/mod/glossary/components/index/index.ts +++ b/src/addon/mod/glossary/components/index/index.ts @@ -109,7 +109,7 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity } } - this.glossaryProvider.logView(this.glossary.id, this.viewMode).then(() => { + this.glossaryProvider.logView(this.glossary.id, this.viewMode, this.glossary.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch((error) => { // Ignore errors. diff --git a/src/addon/mod/glossary/pages/entry/entry.ts b/src/addon/mod/glossary/pages/entry/entry.ts index 03da1c30d..7bbf4f0bf 100644 --- a/src/addon/mod/glossary/pages/entry/entry.ts +++ b/src/addon/mod/glossary/pages/entry/entry.ts @@ -51,7 +51,7 @@ export class AddonModGlossaryEntryPage { */ ionViewDidLoad(): void { this.fetchEntry().then(() => { - this.glossaryProvider.logEntryView(this.entry.id, this.componentId).catch(() => { + this.glossaryProvider.logEntryView(this.entry.id, this.componentId, this.glossary.name).catch(() => { // Ignore errors. }); }).finally(() => { diff --git a/src/addon/mod/glossary/providers/glossary.ts b/src/addon/mod/glossary/providers/glossary.ts index a4014f0d0..1e7ba968b 100644 --- a/src/addon/mod/glossary/providers/glossary.ts +++ b/src/addon/mod/glossary/providers/glossary.ts @@ -867,16 +867,18 @@ export class AddonModGlossaryProvider { * * @param {number} glossaryId Glossary ID. * @param {string} mode The mode in which the glossary was viewed. + * @param {string} [name] Name of the glossary. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(glossaryId: number, mode: string, siteId?: string): Promise { + logView(glossaryId: number, mode: string, name?: string, siteId?: string): Promise { const params = { id: glossaryId, mode: mode }; - return this.logHelper.log('mod_glossary_view_glossary', params, AddonModGlossaryProvider.COMPONENT, glossaryId, siteId); + return this.logHelper.logSingle('mod_glossary_view_glossary', params, AddonModGlossaryProvider.COMPONENT, glossaryId, name, + 'glossary', {mode: mode}, siteId); } /** @@ -884,14 +886,16 @@ export class AddonModGlossaryProvider { * * @param {number} entryId Entry ID. * @param {number} glossaryId Glossary ID. + * @param {string} [name] Name of the glossary. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logEntryView(entryId: number, glossaryId: number, siteId?: string): Promise { + logEntryView(entryId: number, glossaryId: number, name?: string, siteId?: string): Promise { const params = { id: entryId }; - return this.logHelper.log('mod_glossary_view_entry', params, AddonModGlossaryProvider.COMPONENT, glossaryId, siteId); + return this.logHelper.logSingle('mod_glossary_view_entry', params, AddonModGlossaryProvider.COMPONENT, glossaryId, name, + 'glossary', {entryid: entryId}, siteId); } } diff --git a/src/addon/mod/imscp/components/index/index.ts b/src/addon/mod/imscp/components/index/index.ts index 7ca40d13c..11e53d2b2 100644 --- a/src/addon/mod/imscp/components/index/index.ts +++ b/src/addon/mod/imscp/components/index/index.ts @@ -52,7 +52,7 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom super.ngOnInit(); this.loadContent().then(() => { - this.imscpProvider.logView(this.module.instance).then(() => { + this.imscpProvider.logView(this.module.instance, this.module.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch(() => { // Ignore errors. diff --git a/src/addon/mod/imscp/providers/imscp.ts b/src/addon/mod/imscp/providers/imscp.ts index 5cc7b9258..df58e9b46 100644 --- a/src/addon/mod/imscp/providers/imscp.ts +++ b/src/addon/mod/imscp/providers/imscp.ts @@ -309,14 +309,16 @@ export class AddonModImscpProvider { * Report a IMSCP as being viewed. * * @param {string} id Module ID. + * @param {string} [name] Name of the imscp. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params = { imscpid: id }; - return this.logHelper.log('mod_imscp_view_imscp', params, AddonModImscpProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_imscp_view_imscp', params, AddonModImscpProvider.COMPONENT, id, name, 'imscp', {}, + siteId); } } diff --git a/src/addon/mod/lesson/components/index/index.ts b/src/addon/mod/lesson/components/index/index.ts index 3750b56dc..8a0acdba1 100644 --- a/src/addon/mod/lesson/components/index/index.ts +++ b/src/addon/mod/lesson/components/index/index.ts @@ -345,7 +345,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo * Log viewing the lesson. */ protected logView(): void { - this.lessonProvider.logViewLesson(this.lesson.id, this.password).then(() => { + this.lessonProvider.logViewLesson(this.lesson.id, this.password, this.lesson.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch((error) => { // Ignore errors. diff --git a/src/addon/mod/lesson/providers/lesson.ts b/src/addon/mod/lesson/providers/lesson.ts index bfad310ed..55c4763e1 100644 --- a/src/addon/mod/lesson/providers/lesson.ts +++ b/src/addon/mod/lesson/providers/lesson.ts @@ -3017,10 +3017,11 @@ export class AddonModLessonProvider { * * @param {string} id Module ID. * @param {string} [password] Lesson password (if any). + * @param {string} [name] Name of the assign. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logViewLesson(id: number, password?: string, siteId?: string): Promise { + logViewLesson(id: number, password?: string, name?: string, siteId?: string): Promise { return this.sitesProvider.getSite(siteId).then((site) => { const params: any = { lessonid: id @@ -3030,7 +3031,8 @@ export class AddonModLessonProvider { params.password = password; } - return this.logHelper.log('mod_lesson_view_lesson', params, AddonModLessonProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_lesson_view_lesson', params, AddonModLessonProvider.COMPONENT, id, name, + 'lesson', {}, siteId); }); } diff --git a/src/addon/mod/lti/components/index/index.ts b/src/addon/mod/lti/components/index/index.ts index 40ccaa4f0..f61c1e50f 100644 --- a/src/addon/mod/lti/components/index/index.ts +++ b/src/addon/mod/lti/components/index/index.ts @@ -95,7 +95,7 @@ export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityCompo launch(): void { this.ltiProvider.getLtiLaunchData(this.lti.id).then((launchData) => { // "View" LTI. - this.ltiProvider.logView(this.lti.id).then(() => { + this.ltiProvider.logView(this.lti.id, this.lti.name).then(() => { this.checkCompletion(); }).catch((error) => { // Ignore errors. diff --git a/src/addon/mod/lti/providers/lti.ts b/src/addon/mod/lti/providers/lti.ts index 44163818a..fa88da045 100644 --- a/src/addon/mod/lti/providers/lti.ts +++ b/src/addon/mod/lti/providers/lti.ts @@ -213,14 +213,15 @@ export class AddonModLtiProvider { * Report the LTI as being viewed. * * @param {string} id LTI id. + * @param {string} [name] Name of the lti. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params: any = { ltiid: id }; - return this.logHelper.log('mod_lti_view_lti', params, AddonModLtiProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_lti_view_lti', params, AddonModLtiProvider.COMPONENT, id, name, 'lti', {}, siteId); } } diff --git a/src/addon/mod/lti/providers/module-handler.ts b/src/addon/mod/lti/providers/module-handler.ts index 72ca2f2ba..351fd9020 100644 --- a/src/addon/mod/lti/providers/module-handler.ts +++ b/src/addon/mod/lti/providers/module-handler.ts @@ -91,7 +91,7 @@ export class AddonModLtiModuleHandler implements CoreCourseModuleHandler { this.ltiProvider.getLti(courseId, module.id).then((ltiData) => { return this.ltiProvider.getLtiLaunchData(ltiData.id).then((launchData) => { // "View" LTI. - this.ltiProvider.logView(ltiData.id).then(() => { + this.ltiProvider.logView(ltiData.id, ltiData.name).then(() => { this.courseProvider.checkModuleCompletion(courseId, module.completiondata); }).catch(() => { // Ignore errors. diff --git a/src/addon/mod/page/components/index/index.ts b/src/addon/mod/page/components/index/index.ts index 4cf4ef924..254bc0f38 100644 --- a/src/addon/mod/page/components/index/index.ts +++ b/src/addon/mod/page/components/index/index.ts @@ -53,7 +53,7 @@ export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComp this.canGetPage = this.pageProvider.isGetPageWSAvailable(); this.loadContent().then(() => { - this.pageProvider.logView(this.module.instance).then(() => { + this.pageProvider.logView(this.module.instance, this.module.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch(() => { // Ignore errors. diff --git a/src/addon/mod/page/providers/page.ts b/src/addon/mod/page/providers/page.ts index cfdce8b11..a196d9efc 100644 --- a/src/addon/mod/page/providers/page.ts +++ b/src/addon/mod/page/providers/page.ts @@ -148,14 +148,15 @@ export class AddonModPageProvider { * Report a page as being viewed. * * @param {number} id Module ID. + * @param {string} [name] Name of the page. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params = { pageid: id }; - return this.logHelper.log('mod_page_view_page', params, AddonModPageProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_page_view_page', params, AddonModPageProvider.COMPONENT, id, name, 'page', {}, siteId); } } diff --git a/src/addon/mod/quiz/components/index/index.ts b/src/addon/mod/quiz/components/index/index.ts index 30851c387..32eef2714 100644 --- a/src/addon/mod/quiz/components/index/index.ts +++ b/src/addon/mod/quiz/components/index/index.ts @@ -85,7 +85,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp return; } - this.quizProvider.logViewQuiz(this.quizData.id).then(() => { + this.quizProvider.logViewQuiz(this.quizData.id, this.quizData.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch((error) => { // Ignore errors. diff --git a/src/addon/mod/quiz/pages/player/player.ts b/src/addon/mod/quiz/pages/player/player.ts index 12bf38f2b..75da900d8 100644 --- a/src/addon/mod/quiz/pages/player/player.ts +++ b/src/addon/mod/quiz/pages/player/player.ts @@ -457,7 +457,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { }); // Mark the page as viewed. We'll ignore errors in this call. - this.quizProvider.logViewAttempt(this.attempt.id, page, this.preflightData, this.offline).catch((error) => { + this.quizProvider.logViewAttempt(this.attempt.id, page, this.preflightData, this.offline, this.quiz).catch((error) => { // Ignore errors. }); @@ -484,7 +484,8 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { this.attempt.dueDateWarning = this.quizProvider.getAttemptDueDateWarning(this.quiz, this.attempt); // Log summary as viewed. - this.quizProvider.logViewAttemptSummary(this.attempt.id, this.preflightData, this.quizId).catch((error) => { + this.quizProvider.logViewAttemptSummary(this.attempt.id, this.preflightData, this.quizId, this.quiz.name) + .catch((error) => { // Ignore errors. }); }); diff --git a/src/addon/mod/quiz/pages/review/review.ts b/src/addon/mod/quiz/pages/review/review.ts index 6d1bc331d..218d7cd60 100644 --- a/src/addon/mod/quiz/pages/review/review.ts +++ b/src/addon/mod/quiz/pages/review/review.ts @@ -81,7 +81,7 @@ export class AddonModQuizReviewPage implements OnInit { */ ngOnInit(): void { this.fetchData().then(() => { - this.quizProvider.logViewAttemptReview(this.attemptId, this.quizId).catch((error) => { + this.quizProvider.logViewAttemptReview(this.attemptId, this.quizId, this.quiz.name).catch((error) => { // Ignore errors. }); }).finally(() => { diff --git a/src/addon/mod/quiz/providers/quiz-sync.ts b/src/addon/mod/quiz/providers/quiz-sync.ts index e14b3f1d6..9f7226e9d 100644 --- a/src/addon/mod/quiz/providers/quiz-sync.ts +++ b/src/addon/mod/quiz/providers/quiz-sync.ts @@ -392,8 +392,9 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider // Answers sent, now set the current page if the attempt isn't finished. if (!finish) { + // Don't pass the quiz instance because we don't want to trigger a Firebase event in this case. return this.quizProvider.logViewAttempt(onlineAttempt.id, offlineAttempt.currentpage, preflightData, - false).catch(() => { + false, undefined, siteId).catch(() => { // Ignore errors. }); } diff --git a/src/addon/mod/quiz/providers/quiz.ts b/src/addon/mod/quiz/providers/quiz.ts index ee121b63a..00ba2a92e 100644 --- a/src/addon/mod/quiz/providers/quiz.ts +++ b/src/addon/mod/quiz/providers/quiz.ts @@ -27,6 +27,7 @@ import { CoreQuestionDelegate } from '@core/question/providers/delegate'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { AddonModQuizAccessRuleDelegate } from './access-rules-delegate'; import { AddonModQuizOfflineProvider } from './quiz-offline'; +import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; /** * Service that provides some features for quiz. @@ -63,7 +64,8 @@ export class AddonModQuizProvider { private gradesHelper: CoreGradesHelperProvider, private questionDelegate: CoreQuestionDelegate, private filepoolProvider: CoreFilepoolProvider, private timeUtils: CoreTimeUtilsProvider, private accessRulesDelegate: AddonModQuizAccessRuleDelegate, private quizOfflineProvider: AddonModQuizOfflineProvider, - private domUtils: CoreDomUtilsProvider, private logHelper: CoreCourseLogHelperProvider) { + private domUtils: CoreDomUtilsProvider, private logHelper: CoreCourseLogHelperProvider, + protected pushNotificationsProvider: CorePushNotificationsProvider) { this.logger = logger.getInstance('AddonModQuizProvider'); } @@ -1533,10 +1535,13 @@ export class AddonModQuizProvider { * @param {number} [page=0] Page number. * @param {any} [preflightData] Preflight required data (like password). * @param {boolean} [offline] Whether attempt is offline. + * @param {string} [quiz] Quiz instance. If set, a Firebase event will be stored. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logViewAttempt(attemptId: number, page: number = 0, preflightData: any = {}, offline?: boolean, siteId?: string): Promise { + logViewAttempt(attemptId: number, page: number = 0, preflightData: any = {}, offline?: boolean, quiz?: any, + siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { const params = { attemptid: attemptId, @@ -1549,6 +1554,10 @@ export class AddonModQuizProvider { if (offline) { promises.push(this.quizOfflineProvider.setAttemptCurrentPage(attemptId, page, site.getId())); } + if (quiz) { + this.pushNotificationsProvider.logViewEvent(quiz.id, quiz.name, 'quiz', 'mod_quiz_view_attempt', + {attemptid: attemptId, page: page}, siteId); + } return Promise.all(promises); }); @@ -1559,15 +1568,17 @@ export class AddonModQuizProvider { * * @param {number} attemptId Attempt ID. * @param {number} quizId Quiz ID. + * @param {string} [name] Name of the quiz. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logViewAttemptReview(attemptId: number, quizId: number, siteId?: string): Promise { + logViewAttemptReview(attemptId: number, quizId: number, name?: string, siteId?: string): Promise { const params = { attemptid: attemptId }; - return this.logHelper.log('mod_quiz_view_attempt_review', params, AddonModQuizProvider.COMPONENT, quizId, siteId); + return this.logHelper.logSingle('mod_quiz_view_attempt_review', params, AddonModQuizProvider.COMPONENT, quizId, name, + 'quiz', params, siteId); } /** @@ -1576,31 +1587,35 @@ export class AddonModQuizProvider { * @param {number} attemptId Attempt ID. * @param {any} preflightData Preflight required data (like password). * @param {number} quizId Quiz ID. + * @param {string} [name] Name of the quiz. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logViewAttemptSummary(attemptId: number, preflightData: any, quizId: number, siteId?: string): Promise { + logViewAttemptSummary(attemptId: number, preflightData: any, quizId: number, name?: string, siteId?: string): Promise { const params = { attemptid: attemptId, preflightdata: this.utils.objectToArrayOfObjects(preflightData, 'name', 'value', true) }; - return this.logHelper.log('mod_quiz_view_attempt_summary', params, AddonModQuizProvider.COMPONENT, quizId, siteId); + return this.logHelper.logSingle('mod_quiz_view_attempt_summary', params, AddonModQuizProvider.COMPONENT, quizId, name, + 'quiz', {attemptid: attemptId}, siteId); } /** * Report a quiz as being viewed. * * @param {number} id Module ID. + * @param {string} [name] Name of the quiz. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logViewQuiz(id: number, siteId?: string): Promise { + logViewQuiz(id: number, name?: string, siteId?: string): Promise { const params = { quizid: id }; - return this.logHelper.log('mod_quiz_view_quiz', params, AddonModQuizProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_quiz_view_quiz', params, AddonModQuizProvider.COMPONENT, id, name, 'quiz', {}, + siteId); } /** diff --git a/src/addon/mod/resource/components/index/index.ts b/src/addon/mod/resource/components/index/index.ts index 2c1283958..ab8ccffac 100644 --- a/src/addon/mod/resource/components/index/index.ts +++ b/src/addon/mod/resource/components/index/index.ts @@ -53,7 +53,7 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource this.canGetResource = this.resourceProvider.isGetResourceWSAvailable(); this.loadContent().then(() => { - this.resourceProvider.logView(this.module.instance).then(() => { + this.resourceProvider.logView(this.module.instance, this.module.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch(() => { // Ignore errors. diff --git a/src/addon/mod/resource/providers/helper.ts b/src/addon/mod/resource/providers/helper.ts index 3b3f53d4d..0f216e4aa 100644 --- a/src/addon/mod/resource/providers/helper.ts +++ b/src/addon/mod/resource/providers/helper.ts @@ -179,7 +179,7 @@ export class AddonModResourceHelperProvider { // Download and open the file from the resource contents. return this.courseHelper.downloadModuleAndOpenFile(module, courseId, AddonModResourceProvider.COMPONENT, module.id, module.contents).then(() => { - this.resourceProvider.logView(module.instance).then(() => { + this.resourceProvider.logView(module.instance, module.name).then(() => { this.courseProvider.checkModuleCompletion(courseId, module.completiondata); }).catch(() => { // Ignore errors. diff --git a/src/addon/mod/resource/providers/resource.ts b/src/addon/mod/resource/providers/resource.ts index 725cbc473..995092907 100644 --- a/src/addon/mod/resource/providers/resource.ts +++ b/src/addon/mod/resource/providers/resource.ts @@ -150,14 +150,16 @@ export class AddonModResourceProvider { * Report the resource as being viewed. * * @param {number} id Module ID. + * @param {string} [name] Name of the resource. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params = { resourceid: id }; - return this.logHelper.log('mod_resource_view_resource', params, AddonModResourceProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_resource_view_resource', params, AddonModResourceProvider.COMPONENT, id, name, + 'resource', {}, siteId); } } diff --git a/src/addon/mod/scorm/components/index/index.ts b/src/addon/mod/scorm/components/index/index.ts index de725b272..fce78f99c 100644 --- a/src/addon/mod/scorm/components/index/index.ts +++ b/src/addon/mod/scorm/components/index/index.ts @@ -83,7 +83,7 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom return; } - this.scormProvider.logView(this.scorm.id).then(() => { + this.scormProvider.logView(this.scorm.id, this.scorm.name).then(() => { this.checkCompletion(); }).catch((error) => { // Ignore errors. diff --git a/src/addon/mod/scorm/pages/player/player.ts b/src/addon/mod/scorm/pages/player/player.ts index 49f2cd000..63d148aca 100644 --- a/src/addon/mod/scorm/pages/player/player.ts +++ b/src/addon/mod/scorm/pages/player/player.ts @@ -370,7 +370,7 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy { } // Trigger SCO launch event. - this.scormProvider.logLaunchSco(this.scorm.id, sco.id).catch(() => { + this.scormProvider.logLaunchSco(this.scorm.id, sco.id, this.scorm.name).catch(() => { // Ignore errors. }); } diff --git a/src/addon/mod/scorm/providers/scorm.ts b/src/addon/mod/scorm/providers/scorm.ts index 0ee62a7f7..712020716 100644 --- a/src/addon/mod/scorm/providers/scorm.ts +++ b/src/addon/mod/scorm/providers/scorm.ts @@ -1421,21 +1421,19 @@ export class AddonModScormProvider { * * @param {number} scormId SCORM ID. * @param {number} scoId SCO ID. + * @param {string} [name] Name of the SCORM. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logLaunchSco(scormId: number, scoId: number, siteId?: string): Promise { + logLaunchSco(scormId: number, scoId: number, name?: string, siteId?: string): Promise { return this.sitesProvider.getSite(siteId).then((site) => { const params = { scormid: scormId, scoid: scoId }; - return site.write('mod_scorm_launch_sco', params).then((response) => { - if (!response || !response.status) { - return Promise.reject(null); - } - }); + return this.logHelper.logSingle('mod_scorm_launch_sco', params, AddonModScormProvider.COMPONENT, scormId, name, + 'scorm', {scoid: scoId}, siteId); }); } @@ -1443,15 +1441,17 @@ export class AddonModScormProvider { * Report a SCORM as being viewed. * * @param {string} id Module ID. + * @param {string} [name] Name of the SCORM. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params = { scormid: id }; - return this.logHelper.log('mod_scorm_view_scorm', params, AddonModScormProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_scorm_view_scorm', params, AddonModScormProvider.COMPONENT, id, name, 'scorm', {}, + siteId); } /** diff --git a/src/addon/mod/survey/components/index/index.ts b/src/addon/mod/survey/components/index/index.ts index 3a7eb4151..8b6cfbb65 100644 --- a/src/addon/mod/survey/components/index/index.ts +++ b/src/addon/mod/survey/components/index/index.ts @@ -53,7 +53,7 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo this.userId = this.sitesProvider.getCurrentSiteUserId(); this.loadContent(false, true).then(() => { - this.surveyProvider.logView(this.survey.id).then(() => { + this.surveyProvider.logView(this.survey.id, this.survey.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch(() => { // Ignore errors. diff --git a/src/addon/mod/survey/providers/survey.ts b/src/addon/mod/survey/providers/survey.ts index ff376e2f8..1a1d2547f 100644 --- a/src/addon/mod/survey/providers/survey.ts +++ b/src/addon/mod/survey/providers/survey.ts @@ -213,15 +213,17 @@ export class AddonModSurveyProvider { * Report the survey as being viewed. * * @param {number} id Module ID. + * @param {string} [name] Name of the assign. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params = { surveyid: id }; - return this.logHelper.log('mod_survey_view_survey', params, AddonModSurveyProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_survey_view_survey', params, AddonModSurveyProvider.COMPONENT, id, name, 'survey', + {}, siteId); } /** diff --git a/src/addon/mod/url/components/index/index.ts b/src/addon/mod/url/components/index/index.ts index d5c92ec80..842d14194 100644 --- a/src/addon/mod/url/components/index/index.ts +++ b/src/addon/mod/url/components/index/index.ts @@ -178,7 +178,7 @@ export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceCompo * @return {Promise} Promise resolved when done. */ protected logView(): Promise { - return this.urlProvider.logView(this.module.instance).then(() => { + return this.urlProvider.logView(this.module.instance, this.module.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch(() => { // Ignore errors. diff --git a/src/addon/mod/url/providers/module-handler.ts b/src/addon/mod/url/providers/module-handler.ts index a4ba652a5..fb51ea3bc 100644 --- a/src/addon/mod/url/providers/module-handler.ts +++ b/src/addon/mod/url/providers/module-handler.ts @@ -173,7 +173,7 @@ export class AddonModUrlModuleHandler implements CoreCourseModuleHandler { * @param {number} courseId The course ID. */ protected openUrl(module: any, courseId: number): void { - this.urlProvider.logView(module.instance).then(() => { + this.urlProvider.logView(module.instance, module.name).then(() => { this.courseProvider.checkModuleCompletion(courseId, module.completiondata); }).catch(() => { // Ignore errors. diff --git a/src/addon/mod/url/providers/url.ts b/src/addon/mod/url/providers/url.ts index e7e02b85a..3e63ebf9f 100644 --- a/src/addon/mod/url/providers/url.ts +++ b/src/addon/mod/url/providers/url.ts @@ -217,14 +217,15 @@ export class AddonModUrlProvider { * Report the url as being viewed. * * @param {number} id Module ID. + * @param {string} [name] Name of the assign. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params = { urlid: id }; - return this.logHelper.log('mod_url_view_url', params, AddonModUrlProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_url_view_url', params, AddonModUrlProvider.COMPONENT, id, name, 'url', {}, siteId); } } diff --git a/src/addon/mod/wiki/components/index/index.ts b/src/addon/mod/wiki/components/index/index.ts index 25bc9af76..941417a68 100644 --- a/src/addon/mod/wiki/components/index/index.ts +++ b/src/addon/mod/wiki/components/index/index.ts @@ -104,13 +104,13 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp } if (this.isMainPage) { - this.wikiProvider.logView(this.wiki.id).then(() => { + this.wikiProvider.logView(this.wiki.id, this.wiki.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch((error) => { // Ignore errors. }); } else { - this.wikiProvider.logPageView(this.pageId, this.wiki.id).catch(() => { + this.wikiProvider.logPageView(this.pageId, this.wiki.id, this.wiki.name).catch(() => { // Ignore errors. }); } @@ -341,7 +341,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp this.currentPage = data.pageId; this.showLoadingAndFetch(true, false).then(() => { - this.wikiProvider.logPageView(this.currentPage, this.wiki.id).catch(() => { + this.wikiProvider.logPageView(this.currentPage, this.wiki.id, this.wiki.name).catch(() => { // Ignore errors. }); }); diff --git a/src/addon/mod/wiki/providers/wiki.ts b/src/addon/mod/wiki/providers/wiki.ts index 42dcd8b1c..e20543769 100644 --- a/src/addon/mod/wiki/providers/wiki.ts +++ b/src/addon/mod/wiki/providers/wiki.ts @@ -655,30 +655,34 @@ export class AddonModWikiProvider { * * @param {number} id Page ID. * @param {number} wikiId Wiki ID. + * @param {string} [name] Name of the wiki. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logPageView(id: number, wikiId: number, siteId?: string): Promise { + logPageView(id: number, wikiId: number, name?: string, siteId?: string): Promise { const params = { pageid: id }; - return this.logHelper.log('mod_wiki_view_page', params, AddonModWikiProvider.COMPONENT, wikiId, siteId); + return this.logHelper.logSingle('mod_wiki_view_page', params, AddonModWikiProvider.COMPONENT, wikiId, name, 'wiki', + params, siteId); } /** * Report the wiki as being viewed. * * @param {number} id Wiki ID. + * @param {string} [name] Name of the wiki. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params = { wikiid: id }; - return this.logHelper.log('mod_wiki_view_wiki', params, AddonModWikiProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_wiki_view_wiki', params, AddonModWikiProvider.COMPONENT, id, name, 'wiki', {}, + siteId); } /** diff --git a/src/addon/mod/workshop/components/index/index.ts b/src/addon/mod/workshop/components/index/index.ts index 1515e91da..e158670bf 100644 --- a/src/addon/mod/workshop/components/index/index.ts +++ b/src/addon/mod/workshop/components/index/index.ts @@ -107,7 +107,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity return; } - this.workshopProvider.logView(this.workshop.id).then(() => { + this.workshopProvider.logView(this.workshop.id, this.workshop.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch((error) => { // Ignore errors. diff --git a/src/addon/mod/workshop/pages/submission/submission.ts b/src/addon/mod/workshop/pages/submission/submission.ts index e4cb2cd5f..77d82fec2 100644 --- a/src/addon/mod/workshop/pages/submission/submission.ts +++ b/src/addon/mod/workshop/pages/submission/submission.ts @@ -130,7 +130,7 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy { */ ngOnInit(): void { this.fetchSubmissionData().then(() => { - this.workshopProvider.logViewSubmission(this.submissionId, this.workshopId).then(() => { + this.workshopProvider.logViewSubmission(this.submissionId, this.workshopId, this.workshop.name).then(() => { this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); }).catch(() => { // Ignore errors. diff --git a/src/addon/mod/workshop/providers/workshop.ts b/src/addon/mod/workshop/providers/workshop.ts index 5d0e3165e..86e0c3525 100644 --- a/src/addon/mod/workshop/providers/workshop.ts +++ b/src/addon/mod/workshop/providers/workshop.ts @@ -1362,15 +1362,17 @@ export class AddonModWorkshopProvider { * Report the workshop as being viewed. * * @param {number} id Workshop ID. + * @param {string} [name] Name of the workshop. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(id: number, siteId?: string): Promise { + logView(id: number, name?: string, siteId?: string): Promise { const params = { workshopid: id }; - return this.logHelper.log('mod_workshop_view_workshop', params, AddonModWorkshopProvider.COMPONENT, id, siteId); + return this.logHelper.logSingle('mod_workshop_view_workshop', params, AddonModWorkshopProvider.COMPONENT, id, name, + 'workshop', siteId); } /** @@ -1378,14 +1380,16 @@ export class AddonModWorkshopProvider { * * @param {number} id Submission ID. * @param {number} workshopId Workshop ID. + * @param {string} [name] Name of the workshop. * @param {string} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved when the WS call is successful. */ - logViewSubmission(id: number, workshopId: number, siteId?: string): Promise { + logViewSubmission(id: number, workshopId: number, name?: string, siteId?: string): Promise { const params = { submissionid: id }; - return this.logHelper.log('mod_workshop_view_submission', params, AddonModWorkshopProvider.COMPONENT, workshopId, siteId); + return this.logHelper.logSingle('mod_workshop_view_submission', params, AddonModWorkshopProvider.COMPONENT, workshopId, + name, 'workshop', params, siteId); } } diff --git a/src/addon/notes/providers/notes.ts b/src/addon/notes/providers/notes.ts index 8226e0139..78fb79c1c 100644 --- a/src/addon/notes/providers/notes.ts +++ b/src/addon/notes/providers/notes.ts @@ -21,6 +21,7 @@ import { CoreSiteWSPreSets } from '@classes/site'; import { TranslateService } from '@ngx-translate/core'; import { CoreUserProvider } from '@core/user/providers/user'; import { AddonNotesOfflineProvider } from './notes-offline'; +import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; /** * Service to handle notes. @@ -33,7 +34,7 @@ export class AddonNotesProvider { constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private translate: TranslateService, private userProvider: CoreUserProvider, - private notesOffline: AddonNotesOfflineProvider) { + private notesOffline: AddonNotesOfflineProvider, protected pushNotificationsProvider: CorePushNotificationsProvider) { this.logger = logger.getInstance('AddonNotesProvider'); } @@ -318,6 +319,8 @@ export class AddonNotesProvider { userid: userId || 0 }; + this.pushNotificationsProvider.logViewListEvent('notes', 'core_notes_view_notes', params, site.getId()); + return site.write('core_notes_view_notes', params); }); } diff --git a/src/config.json b/src/config.json index cd7c0fd99..57216340b 100644 --- a/src/config.json +++ b/src/config.json @@ -80,5 +80,6 @@ "statusbarbgandroid": "#df7310", "statusbarlighttextandroid": true, "statusbarbgremotetheme": "#000000", - "statusbarlighttextremotetheme": true + "statusbarlighttextremotetheme": true, + "enableanalytics": false } diff --git a/src/core/course/pages/section/section.ts b/src/core/course/pages/section/section.ts index c1dada99d..e8b3d75bb 100644 --- a/src/core/course/pages/section/section.ts +++ b/src/core/course/pages/section/section.ts @@ -213,7 +213,7 @@ export class CoreCourseSectionPage implements OnDestroy { let promise; // Add log in Moodle. - this.courseProvider.logView(this.course.id, this.sectionNumber).catch(() => { + this.courseProvider.logView(this.course.id, this.sectionNumber, undefined, this.course.fullname).catch(() => { // Ignore errors. }); diff --git a/src/core/course/providers/course.ts b/src/core/course/providers/course.ts index c960b7c51..e5207faca 100644 --- a/src/core/course/providers/course.ts +++ b/src/core/course/providers/course.ts @@ -27,6 +27,7 @@ import { CoreConstants } from '../../constants'; import { CoreCourseOfflineProvider } from './course-offline'; import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins'; import { CoreCourseFormatDelegate } from './format-delegate'; +import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; /** * Service that provides some features regarding a course. @@ -102,7 +103,7 @@ export class CoreCourseProvider { private utils: CoreUtilsProvider, private timeUtils: CoreTimeUtilsProvider, private translate: TranslateService, private courseOffline: CoreCourseOfflineProvider, private appProvider: CoreAppProvider, private courseFormatDelegate: CoreCourseFormatDelegate, private sitePluginsProvider: CoreSitePluginsProvider, - private domUtils: CoreDomUtilsProvider) { + private domUtils: CoreDomUtilsProvider, protected pushNotificationsProvider: CorePushNotificationsProvider) { this.logger = logger.getInstance('CoreCourseProvider'); this.sitesProvider.registerSiteSchema(this.siteSchema); @@ -811,18 +812,22 @@ export class CoreCourseProvider { * @param {number} courseId Course ID. * @param {number} [sectionNumber] Section number. * @param {string} [siteId] Site ID. If not defined, current site. + * @param {string} [name] Name of the course. * @return {Promise} Promise resolved when the WS call is successful. */ - logView(courseId: number, sectionNumber?: number, siteId?: string): Promise { + logView(courseId: number, sectionNumber?: number, siteId?: string, name?: string): Promise { const params: any = { - courseid: courseId - }; + courseid: courseId + }, + wsName = 'core_course_view_course'; if (typeof sectionNumber != 'undefined') { params.sectionnumber = sectionNumber; } return this.sitesProvider.getSite(siteId).then((site) => { + this.pushNotificationsProvider.logViewEvent(courseId, name, 'course', wsName, {sectionnumber: sectionNumber}, siteId); + return site.write('core_course_view_course', params).then((response) => { if (!response.status) { return Promise.reject(null); diff --git a/src/core/course/providers/log-cron-handler.ts b/src/core/course/providers/log-cron-handler.ts index 063a37ea6..e708cf056 100644 --- a/src/core/course/providers/log-cron-handler.ts +++ b/src/core/course/providers/log-cron-handler.ts @@ -36,7 +36,7 @@ export class CoreCourseLogCronHandler implements CoreCronHandler { */ execute(siteId?: string, force?: boolean): Promise { return this.sitesProvider.getSite(siteId).then((site) => { - return this.courseProvider.logView(site.getSiteHomeId(), undefined, site.getId()); + return this.courseProvider.logView(site.getSiteHomeId(), undefined, site.getId(), site.getInfo().sitename); }); } diff --git a/src/core/course/providers/log-helper.ts b/src/core/course/providers/log-helper.ts index 89b76231f..36ccf4ab8 100644 --- a/src/core/course/providers/log-helper.ts +++ b/src/core/course/providers/log-helper.ts @@ -18,6 +18,7 @@ import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreAppProvider } from '@providers/app'; +import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; /** * Helper to manage logging to Moodle. @@ -62,7 +63,7 @@ export class CoreCourseLogHelperProvider { constructor(protected sitesProvider: CoreSitesProvider, protected timeUtils: CoreTimeUtilsProvider, protected textUtils: CoreTextUtilsProvider, protected utils: CoreUtilsProvider, - protected appProvider: CoreAppProvider) { + protected appProvider: CoreAppProvider, protected pushNotificationsProvider: CorePushNotificationsProvider) { this.sitesProvider.registerSiteSchema(this.siteSchema); } @@ -196,6 +197,47 @@ export class CoreCourseLogHelperProvider { }); } + /** + * Perform log online. Data will be saved offline for syncing. + * It also triggers a Firebase view_item event. + * + * @param {string} ws WS name. + * @param {any} data Data to send to the WS. + * @param {string} component Component name. + * @param {number} componentId Component ID. + * @param {string} [name] Name of the viewed item. + * @param {string} [category] Category of the viewed item. + * @param {string} [eventData] Data to pass to the Firebase event. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + logSingle(ws: string, data: any, component: string, componentId: number, name?: string, category?: string, eventData?: any, + siteId?: string): Promise { + this.pushNotificationsProvider.logViewEvent(componentId, name, category, ws, eventData, siteId); + + return this.log(ws, data, component, componentId, siteId); + } + + /** + * Perform log online. Data will be saved offline for syncing. + * It also triggers a Firebase view_item_list event. + * + * @param {string} ws WS name. + * @param {any} data Data to send to the WS. + * @param {string} component Component name. + * @param {number} componentId Component ID. + * @param {string} category Category of the viewed item. + * @param {string} [eventData] Data to pass to the Firebase event. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. + */ + logList(ws: string, data: any, component: string, componentId: number, category: string, eventData?: any, siteId?: string) + : Promise { + this.pushNotificationsProvider.logViewListEvent(category, ws, eventData, siteId); + + return this.log(ws, data, component, componentId, siteId); + } + /** * Save activity log for offline sync. * diff --git a/src/core/grades/providers/grades.ts b/src/core/grades/providers/grades.ts index 4647b6ed6..59b25b1d2 100644 --- a/src/core/grades/providers/grades.ts +++ b/src/core/grades/providers/grades.ts @@ -16,6 +16,7 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider } from '@providers/sites'; import { CoreCoursesProvider } from '@core/courses/providers/courses'; +import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; /** * Service to provide grade functionalities. @@ -33,7 +34,7 @@ export class CoreGradesProvider { protected logger; constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, - private coursesProvider: CoreCoursesProvider) { + private coursesProvider: CoreCoursesProvider, protected pushNotificationsProvider: CorePushNotificationsProvider) { this.logger = logger.getInstance('CoreGradesProvider'); } @@ -326,12 +327,25 @@ export class CoreGradesProvider { * * @param {number} courseId Course ID. * @param {number} userId User ID. + * @param {string} [name] Course name. If not set, it will be calculated. * @return {Promise} Promise resolved when done. */ - logCourseGradesView(courseId: number, userId: number): Promise { + logCourseGradesView(courseId: number, userId: number, name?: string): Promise { userId = userId || this.sitesProvider.getCurrentSiteUserId(); - return this.sitesProvider.getCurrentSite().write('gradereport_user_view_grade_report', { + const wsName = 'gradereport_user_view_grade_report'; + + if (!name) { + this.coursesProvider.getUserCourse(courseId, true).catch(() => { + return {}; + }).then((course) => { + this.pushNotificationsProvider.logViewEvent(courseId, course.fullname || '', 'grades', wsName, {userid: userId}); + }); + } else { + this.pushNotificationsProvider.logViewEvent(courseId, name, 'grades', wsName, {userid: userId}); + } + + return this.sitesProvider.getCurrentSite().write(wsName, { courseid: courseId, userid: userId }); @@ -348,8 +362,12 @@ export class CoreGradesProvider { courseId = this.sitesProvider.getCurrentSiteHomeId(); } - return this.sitesProvider.getCurrentSite().write('gradereport_overview_view_grade_report', { + const params = { courseid: courseId - }); + }; + + this.pushNotificationsProvider.logViewListEvent('grades', 'gradereport_overview_view_grade_report', params); + + return this.sitesProvider.getCurrentSite().write('gradereport_overview_view_grade_report', params); } } diff --git a/src/core/pushnotifications/providers/pushnotifications.ts b/src/core/pushnotifications/providers/pushnotifications.ts index de508f430..3a48abfc7 100644 --- a/src/core/pushnotifications/providers/pushnotifications.ts +++ b/src/core/pushnotifications/providers/pushnotifications.ts @@ -292,6 +292,86 @@ export class CorePushNotificationsProvider { return this.getAddonBadge(siteId); } + /** + * Log a firebase event. + * + * @param {string} name Name of the event. + * @param {any} data Data of the event. + * @param {boolean} [filter] Whether to filter the data. This is useful when logging a full notification. + * @return {Promise} Promise resolved when done. This promise is never rejected. + */ + logEvent(name: string, data: any, filter?: boolean): Promise { + const win = window; // This feature is only present in our fork of the plugin. + + if (CoreConfigConstants.enableanalytics && win.PushNotification && win.PushNotification.logEvent) { + return new Promise((resolve, reject): void => { + win.PushNotification.logEvent(resolve, (error) => { + this.logger.error('Error logging firebase event', name, error); + resolve(); + }, name, data, !!filter); + }); + } + + return Promise.resolve(); + } + + /** + * Log a firebase view_item event. + * + * @param {number|string} itemId The item ID. + * @param {string} itemName The item name. + * @param {string} itemCategory The item category. + * @param {string} wsName Name of the WS. + * @param {any} [data] Other data to pass to the event. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. This promise is never rejected. + */ + logViewEvent(itemId: number | string, itemName: string, itemCategory: string, wsName: string, data?: any, siteId?: string) + : Promise { + data = data || {}; + + // Add "moodle" to the name of all extra params. + data = this.utils.prefixKeys(data, 'moodle'); + data['moodleaction'] = wsName; + data['moodlesiteid'] = siteId || this.sitesProvider.getCurrentSiteId(); + + if (itemId) { + data['item_id'] = itemId; + } + if (itemName) { + data['item_name'] = itemName; + } + if (itemCategory) { + data['item_category'] = itemCategory; + } + + return this.logEvent('view_item', data, false); + } + + /** + * Log a firebase view_item_list event. + * + * @param {string} itemCategory The item category. + * @param {string} wsName Name of the WS. + * @param {any} [data] Other data to pass to the event. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when done. This promise is never rejected. + */ + logViewListEvent(itemCategory: string, wsName: string, data?: any, siteId?: string): Promise { + data = data || {}; + + // Add "moodle" to the name of all extra params. + data = this.utils.prefixKeys(data, 'moodle'); + data['moodleaction'] = wsName; + data['moodlesiteid'] = siteId || this.sitesProvider.getCurrentSiteId(); + + if (itemCategory) { + data['item_category'] = itemCategory; + } + + return this.logEvent('view_item_list', data, false); + } + /** * Function called when a push notification is clicked. Redirect the user to the right state. * diff --git a/src/core/pushnotifications/pushnotifications.module.ts b/src/core/pushnotifications/pushnotifications.module.ts index d8d562108..a907ab8ac 100644 --- a/src/core/pushnotifications/pushnotifications.module.ts +++ b/src/core/pushnotifications/pushnotifications.module.ts @@ -80,8 +80,18 @@ export class CorePushNotificationsModule { }); // Listen for local notification clicks (generated by the app). - localNotificationsProvider.registerClick(CorePushNotificationsProvider.COMPONENT, - pushNotificationsProvider.notificationClicked.bind(pushNotificationsProvider)); + localNotificationsProvider.registerClick(CorePushNotificationsProvider.COMPONENT, (notification: any) => { + // Log notification open event. + pushNotificationsProvider.logEvent('moodle_notification_open', notification, true); + + pushNotificationsProvider.notificationClicked(notification); + }); + + // Listen for local notification dismissed events. + localNotificationsProvider.registerObserver('clear', CorePushNotificationsProvider.COMPONENT, (notification: any) => { + // Log notification dismissed event. + pushNotificationsProvider.logEvent('moodle_notification_dismiss', notification, true); + }); // Allow migrating the table from the old app to the new schema. updateManager.registerAppTableMigration({ diff --git a/src/core/sitehome/components/index/index.ts b/src/core/sitehome/components/index/index.ts index 9bf86850d..354881d3b 100644 --- a/src/core/sitehome/components/index/index.ts +++ b/src/core/sitehome/components/index/index.ts @@ -20,6 +20,7 @@ import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; import { CoreBlockDelegate } from '@core/block/providers/delegate'; import { CoreBlockComponent } from '@core/block/components/block/block'; +import { CoreSite } from '@classes/site'; /** * Component that displays site home index. @@ -37,12 +38,14 @@ export class CoreSiteHomeIndexComponent implements OnInit { hasSupportedBlock: boolean; items: any[] = []; siteHomeId: number; + currentSite: CoreSite; blocks = []; - constructor(private domUtils: CoreDomUtilsProvider, private sitesProvider: CoreSitesProvider, + constructor(private domUtils: CoreDomUtilsProvider, sitesProvider: CoreSitesProvider, private courseProvider: CoreCourseProvider, private courseHelper: CoreCourseHelperProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate, private blockDelegate: CoreBlockDelegate) { - this.siteHomeId = sitesProvider.getCurrentSite().getSiteHomeId(); + this.currentSite = sitesProvider.getCurrentSite(); + this.siteHomeId = this.currentSite.getSiteHomeId(); } /** @@ -60,14 +63,13 @@ export class CoreSiteHomeIndexComponent implements OnInit { * @param {any} refresher Refresher. */ doRefresh(refresher: any): void { - const promises = [], - currentSite = this.sitesProvider.getCurrentSite(); + const promises = []; promises.push(this.courseProvider.invalidateSections(this.siteHomeId)); - promises.push(currentSite.invalidateConfig().then(() => { + promises.push(this.currentSite.invalidateConfig().then(() => { // Config invalidated, fetch it again. - return currentSite.getConfig().then((config) => { - currentSite.setConfig(config); + return this.currentSite.getConfig().then((config) => { + this.currentSite.setConfig(config); }); })); @@ -102,7 +104,7 @@ export class CoreSiteHomeIndexComponent implements OnInit { protected loadContent(): Promise { this.hasContent = false; - const config = this.sitesProvider.getCurrentSite().getStoredConfig() || { numsections: 1 }; + const config = this.currentSite.getStoredConfig() || { numsections: 1 }; if (config.frontpageloggedin) { // Items with index 1 and 3 were removed on 2.5 and not being supported in the app. @@ -142,7 +144,8 @@ export class CoreSiteHomeIndexComponent implements OnInit { } // Add log in Moodle. - this.courseProvider.logView(this.siteHomeId).catch(() => { + this.courseProvider.logView(this.siteHomeId, undefined, undefined, + this.currentSite && this.currentSite.getInfo().sitename).catch(() => { // Ignore errors. }); diff --git a/src/core/user/pages/profile/profile.ts b/src/core/user/pages/profile/profile.ts index 46fb41d6a..fd3875f0d 100644 --- a/src/core/user/pages/profile/profile.ts +++ b/src/core/user/pages/profile/profile.ts @@ -84,7 +84,7 @@ export class CoreUserProfilePage { */ ionViewDidLoad(): void { this.fetchUser().then(() => { - return this.userProvider.logView(this.userId, this.courseId).catch((error) => { + return this.userProvider.logView(this.userId, this.courseId, this.user.fullname).catch((error) => { this.isDeleted = error.errorcode === 'userdeleted'; this.isEnrolled = error.errorcode !== 'notenrolledprofile'; }); diff --git a/src/core/user/providers/user.ts b/src/core/user/providers/user.ts index fe1ec6c7c..aa844e9e3 100644 --- a/src/core/user/providers/user.ts +++ b/src/core/user/providers/user.ts @@ -20,6 +20,7 @@ import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreAppProvider } from '@providers/app'; import { CoreUserOfflineProvider } from './offline'; +import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications'; /** * Service to provide user functionalities. @@ -63,7 +64,7 @@ export class CoreUserProvider { constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, private filepoolProvider: CoreFilepoolProvider, private appProvider: CoreAppProvider, - private userOffline: CoreUserOfflineProvider) { + private userOffline: CoreUserOfflineProvider, private pushNotificationsProvider: CorePushNotificationsProvider) { this.logger = logger.getInstance('CoreUserProvider'); this.sitesProvider.registerSiteSchema(this.siteSchema); } @@ -432,18 +433,22 @@ export class CoreUserProvider { * Log User Profile View in Moodle. * @param {number} userId User ID. * @param {number} [courseId] Course ID. + * @param {string} [name] Name of the user. * @return {Promise} Promise resolved when done. */ - logView(userId: number, courseId?: number): Promise { + logView(userId: number, courseId?: number, name?: string): Promise { const params = { - userid: userId - }; + userid: userId + }, + wsName = 'core_user_view_user_profile'; if (courseId) { params['courseid'] = courseId; } - return this.sitesProvider.getCurrentSite().write('core_user_view_user_profile', params); + this.pushNotificationsProvider.logViewEvent(userId, name, 'user', wsName, {courseid: courseId}); + + return this.sitesProvider.getCurrentSite().write(wsName, params); } /** @@ -452,9 +457,13 @@ export class CoreUserProvider { * @return {Promise} Promise resolved when done. */ logParticipantsView(courseId?: number): Promise { - return this.sitesProvider.getCurrentSite().write('core_user_view_user_list', { + const params = { courseid: courseId - }); + }; + + this.pushNotificationsProvider.logViewListEvent('user', 'core_user_view_user_list', params); + + return this.sitesProvider.getCurrentSite().write('core_user_view_user_list', params); } /** diff --git a/src/providers/local-notifications.ts b/src/providers/local-notifications.ts index 036803306..5417b5ff3 100644 --- a/src/providers/local-notifications.ts +++ b/src/providers/local-notifications.ts @@ -102,6 +102,10 @@ export class CoreLocalNotificationsProvider { }; protected triggerSubscription: Subscription; protected clickSubscription: Subscription; + protected clearSubscription: Subscription; + protected cancelSubscription: Subscription; + protected addSubscription: Subscription; + protected updateSubscription: Subscription; constructor(logger: CoreLoggerProvider, private localNotifications: LocalNotifications, private platform: Platform, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private configProvider: CoreConfigProvider, @@ -113,16 +117,31 @@ export class CoreLocalNotificationsProvider { this.appDB.createTablesFromSchema(this.tablesSchema); platform.ready().then(() => { + // Listen to events. this.triggerSubscription = localNotifications.on('trigger').subscribe((notification: ILocalNotification) => { this.trigger(notification); + + this.handleEvent('trigger', notification); }); this.clickSubscription = localNotifications.on('click').subscribe((notification: ILocalNotification) => { - if (notification && notification.data) { - this.logger.debug('Notification clicked: ', notification.data); + this.handleEvent('click', notification); + }); - this.notifyClick(notification.data); - } + this.clearSubscription = localNotifications.on('clear').subscribe((notification: ILocalNotification) => { + this.handleEvent('clear', notification); + }); + + this.cancelSubscription = localNotifications.on('cancel').subscribe((notification: ILocalNotification) => { + this.handleEvent('cancel', notification); + }); + + this.addSubscription = localNotifications.on('add').subscribe((notification: ILocalNotification) => { + this.handleEvent('add', notification); + }); + + this.updateSubscription = localNotifications.on('update').subscribe((notification: ILocalNotification) => { + this.handleEvent('update', notification); }); // Create the default channel for local notifications. @@ -290,6 +309,20 @@ export class CoreLocalNotificationsProvider { }); } + /** + * Handle an event triggered by the local notifications plugin. + * + * @param {string} eventName Name of the event. + * @param {any} notification Notification. + */ + protected handleEvent(eventName: string, notification: any): void { + if (notification && notification.data) { + this.logger.debug('Notification event: ' + eventName + '. Data:', notification.data); + + this.notifyEvent(eventName, notification.data); + } + } + /** * Returns whether local notifications plugin is installed. * @@ -328,12 +361,22 @@ export class CoreLocalNotificationsProvider { * @param {any} data Data received by the notification. */ notifyClick(data: any): void { + this.notifyEvent('click', data); + } + + /** + * Notify a certain event to observers. Only the observers with the same component as the notification will be notified. + * + * @param {string} eventName Name of the event to notify. + * @param {any} data Data received by the notification. + */ + notifyEvent(eventName: string, data: any): void { // Execute the code in the Angular zone, so change detection doesn't stop working. this.zone.run(() => { const component = data.component; if (component) { - if (this.observables[component]) { - this.observables[component].next(data); + if (this.observables[eventName] && this.observables[eventName][component]) { + this.observables[eventName][component].next(data); } } }); @@ -385,18 +428,34 @@ export class CoreLocalNotificationsProvider { * @return {any} Object with an "off" property to stop listening for clicks. */ registerClick(component: string, callback: Function): any { - this.logger.debug(`Register observer '${component}' for notification click.`); + return this.registerObserver('click', component, callback); + } - if (typeof this.observables[component] == 'undefined') { - // No observable for this component, create a new one. - this.observables[component] = new Subject(); + /** + * Register an observer to be notified when a certain event is fired for a notification belonging to a certain component. + * + * @param {string} eventName Name of the event to listen to. + * @param {string} component Component to listen notifications for. + * @param {Function} callback Function to call with the data received by the notification. + * @return {any} Object with an "off" property to stop listening for events. + */ + registerObserver(eventName: string, component: string, callback: Function): any { + this.logger.debug(`Register observer '${component}' for event '${eventName}'.`); + + if (typeof this.observables[eventName] == 'undefined') { + this.observables[eventName] = {}; } - this.observables[component].subscribe(callback); + if (typeof this.observables[eventName][component] == 'undefined') { + // No observable for this component, create a new one. + this.observables[eventName][component] = new Subject(); + } + + this.observables[eventName][component].subscribe(callback); return { off: (): void => { - this.observables[component].unsubscribe(callback); + this.observables[eventName][component].unsubscribe(callback); } }; } diff --git a/src/providers/utils/utils.ts b/src/providers/utils/utils.ts index 5c2475fd1..1569b505d 100644 --- a/src/providers/utils/utils.ts +++ b/src/providers/utils/utils.ts @@ -1049,6 +1049,24 @@ export class CoreUtilsProvider { return mapped; } + /** + * Add a prefix to all the keys in an object. + * + * @param {any} data Object. + * @param {string} prefix Prefix to add. + * @return {any} Prefixed object. + */ + prefixKeys(data: any, prefix: string): any { + const newObj = {}, + keys = Object.keys(data); + + keys.forEach((key) => { + newObj[prefix + key] = data[key]; + }); + + return newObj; + } + /** * Similar to AngularJS $q.defer(). *