Merge pull request #1925 from dpalou/MOBILE-2957

Mobile 2957
main
Juan Leyva 2019-05-20 13:11:36 +02:00 committed by GitHub
commit 7c09d55ea8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 503 additions and 162 deletions

View File

@ -16,6 +16,7 @@ import { Injectable } from '@angular/core';
import { CoreLoggerProvider } from '@providers/logger'; import { CoreLoggerProvider } from '@providers/logger';
import { CoreSitesProvider } from '@providers/sites'; import { CoreSitesProvider } from '@providers/sites';
import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreUtilsProvider } from '@providers/utils/utils';
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
/** /**
* Service to handle blog entries. * Service to handle blog entries.
@ -27,7 +28,8 @@ export class AddonBlogProvider {
protected ROOT_CACHE_KEY = 'addonBlog:'; protected ROOT_CACHE_KEY = 'addonBlog:';
protected logger; 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'); this.logger = logger.getInstance('AddonBlogProvider');
} }
@ -102,6 +104,8 @@ export class AddonBlogProvider {
* @return {Promise<any>} Promise to be resolved when done. * @return {Promise<any>} Promise to be resolved when done.
*/ */
logView(filter: any = {}, siteId?: string): Promise<any> { logView(filter: any = {}, siteId?: string): Promise<any> {
this.pushNotificationsProvider.logViewListEvent('blog', 'core_blog_view_entries', filter, siteId);
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const data = { const data = {
filters: this.utils.objectToArrayOfObjects(filter, 'name', 'value') filters: this.utils.objectToArrayOfObjects(filter, 'name', 'value')

View File

@ -55,13 +55,16 @@ export class AddonCompetencyCompetencyPage {
*/ */
ionViewDidLoad(): void { ionViewDidLoad(): void {
this.fetchCompetency().then(() => { this.fetchCompetency().then(() => {
const name = this.competency && this.competency.competency && this.competency.competency.competency &&
this.competency.competency.competency.shortname;
if (this.planId) { if (this.planId) {
this.competencyProvider.logCompetencyInPlanView(this.planId, this.competencyId, this.planStatus, this.userId) this.competencyProvider.logCompetencyInPlanView(this.planId, this.competencyId, this.planStatus, name,
.catch(() => { this.userId).catch(() => {
// Ignore errors. // Ignore errors.
}); });
} else { } else {
this.competencyProvider.logCompetencyInCourseView(this.courseId, this.competencyId, this.userId).catch(() => { this.competencyProvider.logCompetencyInCourseView(this.courseId, this.competencyId, name, this.userId).catch(() => {
// Ignore errors. // Ignore errors.
}); });
} }

View File

@ -41,7 +41,10 @@ export class AddonCompetencyCompetencySummaryPage {
*/ */
ionViewDidLoad(): void { ionViewDidLoad(): void {
this.fetchCompetency().then(() => { 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. // Ignore errors.
}); });
}).finally(() => { }).finally(() => {

View File

@ -15,6 +15,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CoreLoggerProvider } from '@providers/logger'; import { CoreLoggerProvider } from '@providers/logger';
import { CoreSitesProvider } from '@providers/sites'; import { CoreSitesProvider } from '@providers/sites';
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
/** /**
* Service to handle caompetency learning plans. * Service to handle caompetency learning plans.
@ -38,7 +39,8 @@ export class AddonCompetencyProvider {
protected logger; protected logger;
constructor(loggerProvider: CoreLoggerProvider, private sitesProvider: CoreSitesProvider) { constructor(loggerProvider: CoreLoggerProvider, private sitesProvider: CoreSitesProvider,
protected pushNotificationsProvider: CorePushNotificationsProvider) {
this.logger = loggerProvider.getInstance('AddonCompetencyProvider'); this.logger = loggerProvider.getInstance('AddonCompetencyProvider');
} }
@ -471,13 +473,15 @@ export class AddonCompetencyProvider {
* @param {number} planId ID of the plan. * @param {number} planId ID of the plan.
* @param {number} competencyId ID of the competency. * @param {number} competencyId ID of the competency.
* @param {number} planStatus Current plan Status to decide what action should be logged. * @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 {number} [userId] User ID. If not defined, current user.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logCompetencyInPlanView(planId: number, competencyId: number, planStatus: number, userId?: number, siteId?: string) logCompetencyInPlanView(planId: number, competencyId: number, planStatus: number, name?: string, userId?: number,
: Promise<any> { siteId?: string): Promise<any> {
if (planId && competencyId) { if (planId && competencyId) {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
userId = userId || site.getUserId(); userId = userId || site.getUserId();
@ -488,13 +492,17 @@ export class AddonCompetencyProvider {
}, },
preSets = { preSets = {
typeExpected: 'boolean' 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) { this.pushNotificationsProvider.logViewEvent(competencyId, name, 'competency', wsName, {
return site.write('core_competency_user_competency_plan_viewed', params, preSets); planid: planId,
} else { planstatus: planStatus,
return site.write('core_competency_user_competency_viewed_in_plan', params, preSets); 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} courseId ID of the course.
* @param {number} competencyId ID of the competency. * @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 {number} [userId] User ID. If not defined, current user.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logCompetencyInCourseView(courseId: number, competencyId: number, userId?: number, siteId?: string): Promise<any> { logCompetencyInCourseView(courseId: number, competencyId: number, name?: string, userId?: number, siteId?: string)
: Promise<any> {
if (courseId && competencyId) { if (courseId && competencyId) {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
userId = userId || site.getUserId(); userId = userId || site.getUserId();
@ -523,8 +534,14 @@ export class AddonCompetencyProvider {
const preSets = { const preSets = {
typeExpected: 'boolean' 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. * Report the competency as being viewed.
* *
* @param {number} competencyId ID of the competency. * @param {number} competencyId ID of the competency.
* @param {string} [name] Name of the competency.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logCompetencyView(competencyId: number, siteId?: string): Promise<any> { logCompetencyView(competencyId: number, name?: string, siteId?: string): Promise<any> {
if (competencyId) { if (competencyId) {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const params = { const params = {
@ -547,6 +565,9 @@ export class AddonCompetencyProvider {
const preSets = { const preSets = {
typeExpected: 'boolean' 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); return site.write('core_competency_competency_viewed', params, preSets);
}); });

View File

@ -80,7 +80,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
this.userId = this.sitesProvider.getCurrentSiteUserId(); this.userId = this.sitesProvider.getCurrentSiteUserId();
this.loadContent(false, true).then(() => { 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.
@ -88,12 +88,12 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
if (this.canViewAllSubmissions) { if (this.canViewAllSubmissions) {
// User can see all submissions, log grading view. // 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. // Ignore errors.
}); });
} else if (this.canViewOwnSubmission) { } else if (this.canViewOwnSubmission) {
// User can only see their own submission, log view the user submission. // 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. // Ignore errors.
}); });
} }

View File

@ -911,16 +911,19 @@ export class AddonModAssignProvider {
* Report an assignment submission as being viewed. * Report an assignment submission as being viewed.
* *
* @param {number} assignId Assignment ID. * @param {number} assignId Assignment ID.
* @param {string} [name] Name of the assign.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logSubmissionView(assignId: number, siteId?: string): Promise<any> { logSubmissionView(assignId: number, name?: string, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const params = { const params = {
assignid: assignId 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. * Report an assignment grading table is being viewed.
* *
* @param {number} assignId Assignment ID. * @param {number} assignId Assignment ID.
* @param {string} [name] Name of the assign.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logGradingView(assignId: number, siteId?: string): Promise<any> { logGradingView(assignId: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
assignid: assignId 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. * Report an assign as being viewed.
* *
* @param {number} assignId Assignment ID. * @param {number} assignId Assignment ID.
* @param {string} [name] Name of the assign.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(assignId: number, siteId?: string): Promise<any> { logView(assignId: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
assignid: assignId 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);
} }
/** /**

View File

@ -184,7 +184,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
this.nextChapter = this.bookProvider.getNextChapter(this.chapters, chapterId); 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. // 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. // Module is completed when last chapter is viewed, so we only check completion if the last is reached.
if (!this.nextChapter) { if (!this.nextChapter) {
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);

View File

@ -380,15 +380,17 @@ export class AddonModBookProvider {
* *
* @param {number} id Module ID. * @param {number} id Module ID.
* @param {string} chapterId Chapter ID. * @param {string} chapterId Chapter ID.
* @param {string} [name] Name of the book.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, chapterId: string, siteId?: string): Promise<any> { logView(id: number, chapterId: string, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
bookid: id, bookid: id,
chapterid: chapterId 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);
} }
} }

View File

@ -47,7 +47,7 @@ export class AddonModChatIndexComponent extends CoreCourseModuleMainActivityComp
super.ngOnInit(); super.ngOnInit();
this.loadContent().then(() => { 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.

View File

@ -87,15 +87,16 @@ export class AddonModChatProvider {
* Report a chat as being viewed. * Report a chat as being viewed.
* *
* @param {number} id Chat instance ID. * @param {number} id Chat instance ID.
* @param {string} [name] Name of the chat.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
chatid: id 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);
} }
/** /**

View File

@ -67,7 +67,7 @@ export class AddonModChoiceIndexComponent extends CoreCourseModuleMainActivityCo
if (!this.choice) { if (!this.choice) {
return; 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch((error) => { }).catch((error) => {
// Ignore errors. // Ignore errors.

View File

@ -371,15 +371,17 @@ export class AddonModChoiceProvider {
* Report the choice as being viewed. * Report the choice as being viewed.
* *
* @param {string} id Choice ID. * @param {string} id Choice ID.
* @param {string} [name] Name of the choice.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
choiceid: id 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);
} }
/** /**

View File

@ -129,7 +129,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
return; 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.

View File

@ -928,15 +928,17 @@ export class AddonModDataProvider {
* Report the database as being viewed. * Report the database as being viewed.
* *
* @param {number} id Module ID. * @param {number} id Module ID.
* @param {string} [name] Name of the data.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
databaseid: id 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);
} }
/** /**

View File

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

View File

@ -95,7 +95,7 @@ export class AddonModFeedbackFormPage implements OnDestroy {
*/ */
ionViewDidLoad(): void { ionViewDidLoad(): void {
this.fetchData().then(() => { 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.

View File

@ -1124,17 +1124,19 @@ export class AddonModFeedbackProvider {
* Report the feedback as being viewed. * Report the feedback as being viewed.
* *
* @param {number} id Module ID. * @param {number} id Module ID.
* @param {string} [name] Name of the feedback.
* @param {boolean} [formViewed=false] True if form was viewed. * @param {boolean} [formViewed=false] True if form was viewed.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, formViewed: boolean = false, siteId?: string): Promise<any> { logView(id: number, name?: string, formViewed: boolean = false, siteId?: string): Promise<any> {
const params = { const params = {
feedbackid: id, feedbackid: id,
moduleviewed: formViewed ? 1 : 0 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);
} }
/** /**

View File

@ -55,7 +55,7 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
this.refreshIcon = 'refresh'; this.refreshIcon = 'refresh';
} else { } else {
this.loadContent().then(() => { 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.

View File

@ -133,14 +133,16 @@ export class AddonModFolderProvider {
* Report a folder as being viewed. * Report a folder as being viewed.
* *
* @param {number} id Module ID. * @param {number} id Module ID.
* @param {string} [name] Name of the folder.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
folderid: id 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);
} }
} }

View File

@ -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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch((error) => { }).catch((error) => {
// Ignore errors. // Ignore errors.

View File

@ -384,7 +384,7 @@ export class AddonModForumDiscussionPage implements OnDestroy {
if (forceMarkAsRead || (hasUnreadPosts && this.trackPosts)) { if (forceMarkAsRead || (hasUnreadPosts && this.trackPosts)) {
// // Add log in Moodle and mark unread posts as readed. // // 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. // Ignore errors.
}).finally(() => { }).finally(() => {
// Trigger mark read posts. // Trigger mark read posts.

View File

@ -771,15 +771,17 @@ export class AddonModForumProvider {
* Report a forum as being viewed. * Report a forum as being viewed.
* *
* @param {number} id Module ID. * @param {number} id Module ID.
* @param {string} [name] Name of the forum.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
forumid: id 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} id Discussion ID.
* @param {number} forumId Forum ID. * @param {number} forumId Forum ID.
* @param {string} [name] Name of the forum.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logDiscussionView(id: number, forumId: number, siteId?: string): Promise<any> { logDiscussionView(id: number, forumId: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
discussionid: id 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);
} }
/** /**

View File

@ -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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch((error) => { }).catch((error) => {
// Ignore errors. // Ignore errors.

View File

@ -51,7 +51,7 @@ export class AddonModGlossaryEntryPage {
*/ */
ionViewDidLoad(): void { ionViewDidLoad(): void {
this.fetchEntry().then(() => { 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. // Ignore errors.
}); });
}).finally(() => { }).finally(() => {

View File

@ -867,16 +867,18 @@ export class AddonModGlossaryProvider {
* *
* @param {number} glossaryId Glossary ID. * @param {number} glossaryId Glossary ID.
* @param {string} mode The mode in which the glossary was viewed. * @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. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(glossaryId: number, mode: string, siteId?: string): Promise<any> { logView(glossaryId: number, mode: string, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
id: glossaryId, id: glossaryId,
mode: mode 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} entryId Entry ID.
* @param {number} glossaryId Glossary ID. * @param {number} glossaryId Glossary ID.
* @param {string} [name] Name of the glossary.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logEntryView(entryId: number, glossaryId: number, siteId?: string): Promise<any> { logEntryView(entryId: number, glossaryId: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
id: entryId 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);
} }
} }

View File

@ -52,7 +52,7 @@ export class AddonModImscpIndexComponent extends CoreCourseModuleMainResourceCom
super.ngOnInit(); super.ngOnInit();
this.loadContent().then(() => { 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.

View File

@ -309,14 +309,16 @@ export class AddonModImscpProvider {
* Report a IMSCP as being viewed. * Report a IMSCP as being viewed.
* *
* @param {string} id Module ID. * @param {string} id Module ID.
* @param {string} [name] Name of the imscp.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
imscpid: id 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);
} }
} }

View File

@ -345,7 +345,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo
* Log viewing the lesson. * Log viewing the lesson.
*/ */
protected logView(): void { 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch((error) => { }).catch((error) => {
// Ignore errors. // Ignore errors.

View File

@ -3017,10 +3017,11 @@ export class AddonModLessonProvider {
* *
* @param {string} id Module ID. * @param {string} id Module ID.
* @param {string} [password] Lesson password (if any). * @param {string} [password] Lesson password (if any).
* @param {string} [name] Name of the assign.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logViewLesson(id: number, password?: string, siteId?: string): Promise<any> { logViewLesson(id: number, password?: string, name?: string, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const params: any = { const params: any = {
lessonid: id lessonid: id
@ -3030,7 +3031,8 @@ export class AddonModLessonProvider {
params.password = password; 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);
}); });
} }

View File

@ -95,7 +95,7 @@ export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityCompo
launch(): void { launch(): void {
this.ltiProvider.getLtiLaunchData(this.lti.id).then((launchData) => { this.ltiProvider.getLtiLaunchData(this.lti.id).then((launchData) => {
// "View" LTI. // "View" LTI.
this.ltiProvider.logView(this.lti.id).then(() => { this.ltiProvider.logView(this.lti.id, this.lti.name).then(() => {
this.checkCompletion(); this.checkCompletion();
}).catch((error) => { }).catch((error) => {
// Ignore errors. // Ignore errors.

View File

@ -213,14 +213,15 @@ export class AddonModLtiProvider {
* Report the LTI as being viewed. * Report the LTI as being viewed.
* *
* @param {string} id LTI id. * @param {string} id LTI id.
* @param {string} [name] Name of the lti.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params: any = { const params: any = {
ltiid: id 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);
} }
} }

View File

@ -91,7 +91,7 @@ export class AddonModLtiModuleHandler implements CoreCourseModuleHandler {
this.ltiProvider.getLti(courseId, module.id).then((ltiData) => { this.ltiProvider.getLti(courseId, module.id).then((ltiData) => {
return this.ltiProvider.getLtiLaunchData(ltiData.id).then((launchData) => { return this.ltiProvider.getLtiLaunchData(ltiData.id).then((launchData) => {
// "View" LTI. // "View" LTI.
this.ltiProvider.logView(ltiData.id).then(() => { this.ltiProvider.logView(ltiData.id, ltiData.name).then(() => {
this.courseProvider.checkModuleCompletion(courseId, module.completiondata); this.courseProvider.checkModuleCompletion(courseId, module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.

View File

@ -53,7 +53,7 @@ export class AddonModPageIndexComponent extends CoreCourseModuleMainResourceComp
this.canGetPage = this.pageProvider.isGetPageWSAvailable(); this.canGetPage = this.pageProvider.isGetPageWSAvailable();
this.loadContent().then(() => { 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.

View File

@ -148,14 +148,15 @@ export class AddonModPageProvider {
* Report a page as being viewed. * Report a page as being viewed.
* *
* @param {number} id Module ID. * @param {number} id Module ID.
* @param {string} [name] Name of the page.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
pageid: id 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);
} }
} }

View File

@ -85,7 +85,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
return; 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch((error) => { }).catch((error) => {
// Ignore errors. // Ignore errors.

View File

@ -457,7 +457,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy {
}); });
// Mark the page as viewed. We'll ignore errors in this call. // 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. // Ignore errors.
}); });
@ -484,7 +484,8 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy {
this.attempt.dueDateWarning = this.quizProvider.getAttemptDueDateWarning(this.quiz, this.attempt); this.attempt.dueDateWarning = this.quizProvider.getAttemptDueDateWarning(this.quiz, this.attempt);
// Log summary as viewed. // 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. // Ignore errors.
}); });
}); });

View File

@ -81,7 +81,7 @@ export class AddonModQuizReviewPage implements OnInit {
*/ */
ngOnInit(): void { ngOnInit(): void {
this.fetchData().then(() => { 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. // Ignore errors.
}); });
}).finally(() => { }).finally(() => {

View File

@ -392,8 +392,9 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider
// Answers sent, now set the current page if the attempt isn't finished. // Answers sent, now set the current page if the attempt isn't finished.
if (!finish) { 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, return this.quizProvider.logViewAttempt(onlineAttempt.id, offlineAttempt.currentpage, preflightData,
false).catch(() => { false, undefined, siteId).catch(() => {
// Ignore errors. // Ignore errors.
}); });
} }

View File

@ -27,6 +27,7 @@ import { CoreQuestionDelegate } from '@core/question/providers/delegate';
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
import { AddonModQuizAccessRuleDelegate } from './access-rules-delegate'; import { AddonModQuizAccessRuleDelegate } from './access-rules-delegate';
import { AddonModQuizOfflineProvider } from './quiz-offline'; import { AddonModQuizOfflineProvider } from './quiz-offline';
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
/** /**
* Service that provides some features for quiz. * Service that provides some features for quiz.
@ -63,7 +64,8 @@ export class AddonModQuizProvider {
private gradesHelper: CoreGradesHelperProvider, private questionDelegate: CoreQuestionDelegate, private gradesHelper: CoreGradesHelperProvider, private questionDelegate: CoreQuestionDelegate,
private filepoolProvider: CoreFilepoolProvider, private timeUtils: CoreTimeUtilsProvider, private filepoolProvider: CoreFilepoolProvider, private timeUtils: CoreTimeUtilsProvider,
private accessRulesDelegate: AddonModQuizAccessRuleDelegate, private quizOfflineProvider: AddonModQuizOfflineProvider, 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'); this.logger = logger.getInstance('AddonModQuizProvider');
} }
@ -1533,10 +1535,13 @@ export class AddonModQuizProvider {
* @param {number} [page=0] Page number. * @param {number} [page=0] Page number.
* @param {any} [preflightData] Preflight required data (like password). * @param {any} [preflightData] Preflight required data (like password).
* @param {boolean} [offline] Whether attempt is offline. * @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. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logViewAttempt(attemptId: number, page: number = 0, preflightData: any = {}, offline?: boolean, siteId?: string): Promise<any> { logViewAttempt(attemptId: number, page: number = 0, preflightData: any = {}, offline?: boolean, quiz?: any,
siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const params = { const params = {
attemptid: attemptId, attemptid: attemptId,
@ -1549,6 +1554,10 @@ export class AddonModQuizProvider {
if (offline) { if (offline) {
promises.push(this.quizOfflineProvider.setAttemptCurrentPage(attemptId, page, site.getId())); 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); return Promise.all(promises);
}); });
@ -1559,15 +1568,17 @@ export class AddonModQuizProvider {
* *
* @param {number} attemptId Attempt ID. * @param {number} attemptId Attempt ID.
* @param {number} quizId Quiz ID. * @param {number} quizId Quiz ID.
* @param {string} [name] Name of the quiz.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logViewAttemptReview(attemptId: number, quizId: number, siteId?: string): Promise<any> { logViewAttemptReview(attemptId: number, quizId: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
attemptid: attemptId 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 {number} attemptId Attempt ID.
* @param {any} preflightData Preflight required data (like password). * @param {any} preflightData Preflight required data (like password).
* @param {number} quizId Quiz ID. * @param {number} quizId Quiz ID.
* @param {string} [name] Name of the quiz.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logViewAttemptSummary(attemptId: number, preflightData: any, quizId: number, siteId?: string): Promise<any> { logViewAttemptSummary(attemptId: number, preflightData: any, quizId: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
attemptid: attemptId, attemptid: attemptId,
preflightdata: this.utils.objectToArrayOfObjects(preflightData, 'name', 'value', true) 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. * Report a quiz as being viewed.
* *
* @param {number} id Module ID. * @param {number} id Module ID.
* @param {string} [name] Name of the quiz.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logViewQuiz(id: number, siteId?: string): Promise<any> { logViewQuiz(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
quizid: id 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);
} }
/** /**

View File

@ -53,7 +53,7 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
this.canGetResource = this.resourceProvider.isGetResourceWSAvailable(); this.canGetResource = this.resourceProvider.isGetResourceWSAvailable();
this.loadContent().then(() => { 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.

View File

@ -179,7 +179,7 @@ export class AddonModResourceHelperProvider {
// Download and open the file from the resource contents. // Download and open the file from the resource contents.
return this.courseHelper.downloadModuleAndOpenFile(module, courseId, AddonModResourceProvider.COMPONENT, module.id, return this.courseHelper.downloadModuleAndOpenFile(module, courseId, AddonModResourceProvider.COMPONENT, module.id,
module.contents).then(() => { module.contents).then(() => {
this.resourceProvider.logView(module.instance).then(() => { this.resourceProvider.logView(module.instance, module.name).then(() => {
this.courseProvider.checkModuleCompletion(courseId, module.completiondata); this.courseProvider.checkModuleCompletion(courseId, module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.

View File

@ -150,14 +150,16 @@ export class AddonModResourceProvider {
* Report the resource as being viewed. * Report the resource as being viewed.
* *
* @param {number} id Module ID. * @param {number} id Module ID.
* @param {string} [name] Name of the resource.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
resourceid: id 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);
} }
} }

View File

@ -83,7 +83,7 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom
return; return;
} }
this.scormProvider.logView(this.scorm.id).then(() => { this.scormProvider.logView(this.scorm.id, this.scorm.name).then(() => {
this.checkCompletion(); this.checkCompletion();
}).catch((error) => { }).catch((error) => {
// Ignore errors. // Ignore errors.

View File

@ -370,7 +370,7 @@ export class AddonModScormPlayerPage implements OnInit, OnDestroy {
} }
// Trigger SCO launch event. // 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. // Ignore errors.
}); });
} }

View File

@ -1421,21 +1421,19 @@ export class AddonModScormProvider {
* *
* @param {number} scormId SCORM ID. * @param {number} scormId SCORM ID.
* @param {number} scoId SCO ID. * @param {number} scoId SCO ID.
* @param {string} [name] Name of the SCORM.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logLaunchSco(scormId: number, scoId: number, siteId?: string): Promise<any> { logLaunchSco(scormId: number, scoId: number, name?: string, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const params = { const params = {
scormid: scormId, scormid: scormId,
scoid: scoId scoid: scoId
}; };
return site.write('mod_scorm_launch_sco', params).then((response) => { return this.logHelper.logSingle('mod_scorm_launch_sco', params, AddonModScormProvider.COMPONENT, scormId, name,
if (!response || !response.status) { 'scorm', {scoid: scoId}, siteId);
return Promise.reject(null);
}
});
}); });
} }
@ -1443,15 +1441,17 @@ export class AddonModScormProvider {
* Report a SCORM as being viewed. * Report a SCORM as being viewed.
* *
* @param {string} id Module ID. * @param {string} id Module ID.
* @param {string} [name] Name of the SCORM.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
scormid: id 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);
} }
/** /**

View File

@ -53,7 +53,7 @@ export class AddonModSurveyIndexComponent extends CoreCourseModuleMainActivityCo
this.userId = this.sitesProvider.getCurrentSiteUserId(); this.userId = this.sitesProvider.getCurrentSiteUserId();
this.loadContent(false, true).then(() => { 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.

View File

@ -213,15 +213,17 @@ export class AddonModSurveyProvider {
* Report the survey as being viewed. * Report the survey as being viewed.
* *
* @param {number} id Module ID. * @param {number} id Module ID.
* @param {string} [name] Name of the assign.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
surveyid: id 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);
} }
/** /**

View File

@ -178,7 +178,7 @@ export class AddonModUrlIndexComponent extends CoreCourseModuleMainResourceCompo
* @return {Promise<void>} Promise resolved when done. * @return {Promise<void>} Promise resolved when done.
*/ */
protected logView(): Promise<void> { protected logView(): Promise<void> {
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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.

View File

@ -173,7 +173,7 @@ export class AddonModUrlModuleHandler implements CoreCourseModuleHandler {
* @param {number} courseId The course ID. * @param {number} courseId The course ID.
*/ */
protected openUrl(module: any, courseId: number): void { 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); this.courseProvider.checkModuleCompletion(courseId, module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.

View File

@ -217,14 +217,15 @@ export class AddonModUrlProvider {
* Report the url as being viewed. * Report the url as being viewed.
* *
* @param {number} id Module ID. * @param {number} id Module ID.
* @param {string} [name] Name of the assign.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
urlid: id 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);
} }
} }

View File

@ -104,13 +104,13 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp
} }
if (this.isMainPage) { 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch((error) => { }).catch((error) => {
// Ignore errors. // Ignore errors.
}); });
} else { } else {
this.wikiProvider.logPageView(this.pageId, this.wiki.id).catch(() => { this.wikiProvider.logPageView(this.pageId, this.wiki.id, this.wiki.name).catch(() => {
// Ignore errors. // Ignore errors.
}); });
} }
@ -341,7 +341,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp
this.currentPage = data.pageId; this.currentPage = data.pageId;
this.showLoadingAndFetch(true, false).then(() => { 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. // Ignore errors.
}); });
}); });

View File

@ -655,30 +655,34 @@ export class AddonModWikiProvider {
* *
* @param {number} id Page ID. * @param {number} id Page ID.
* @param {number} wikiId Wiki ID. * @param {number} wikiId Wiki ID.
* @param {string} [name] Name of the wiki.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logPageView(id: number, wikiId: number, siteId?: string): Promise<any> { logPageView(id: number, wikiId: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
pageid: id 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. * Report the wiki as being viewed.
* *
* @param {number} id Wiki ID. * @param {number} id Wiki ID.
* @param {string} [name] Name of the wiki.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
wikiid: id 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);
} }
/** /**

View File

@ -107,7 +107,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity
return; 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch((error) => { }).catch((error) => {
// Ignore errors. // Ignore errors.

View File

@ -130,7 +130,7 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy {
*/ */
ngOnInit(): void { ngOnInit(): void {
this.fetchSubmissionData().then(() => { 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); this.courseProvider.checkModuleCompletion(this.courseId, this.module.completiondata);
}).catch(() => { }).catch(() => {
// Ignore errors. // Ignore errors.

View File

@ -1362,15 +1362,17 @@ export class AddonModWorkshopProvider {
* Report the workshop as being viewed. * Report the workshop as being viewed.
* *
* @param {number} id Workshop ID. * @param {number} id Workshop ID.
* @param {string} [name] Name of the workshop.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logView(id: number, siteId?: string): Promise<any> { logView(id: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
workshopid: id 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} id Submission ID.
* @param {number} workshopId Workshop ID. * @param {number} workshopId Workshop ID.
* @param {string} [name] Name of the workshop.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the WS call is successful. * @return {Promise<any>} Promise resolved when the WS call is successful.
*/ */
logViewSubmission(id: number, workshopId: number, siteId?: string): Promise<any> { logViewSubmission(id: number, workshopId: number, name?: string, siteId?: string): Promise<any> {
const params = { const params = {
submissionid: id 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);
} }
} }

View File

@ -21,6 +21,7 @@ import { CoreSiteWSPreSets } from '@classes/site';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { CoreUserProvider } from '@core/user/providers/user'; import { CoreUserProvider } from '@core/user/providers/user';
import { AddonNotesOfflineProvider } from './notes-offline'; import { AddonNotesOfflineProvider } from './notes-offline';
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
/** /**
* Service to handle notes. * Service to handle notes.
@ -33,7 +34,7 @@ export class AddonNotesProvider {
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider, constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider,
private utils: CoreUtilsProvider, private translate: TranslateService, private userProvider: CoreUserProvider, private utils: CoreUtilsProvider, private translate: TranslateService, private userProvider: CoreUserProvider,
private notesOffline: AddonNotesOfflineProvider) { private notesOffline: AddonNotesOfflineProvider, protected pushNotificationsProvider: CorePushNotificationsProvider) {
this.logger = logger.getInstance('AddonNotesProvider'); this.logger = logger.getInstance('AddonNotesProvider');
} }
@ -318,6 +319,8 @@ export class AddonNotesProvider {
userid: userId || 0 userid: userId || 0
}; };
this.pushNotificationsProvider.logViewListEvent('notes', 'core_notes_view_notes', params, site.getId());
return site.write('core_notes_view_notes', params); return site.write('core_notes_view_notes', params);
}); });
} }

View File

@ -80,5 +80,6 @@
"statusbarbgandroid": "#df7310", "statusbarbgandroid": "#df7310",
"statusbarlighttextandroid": true, "statusbarlighttextandroid": true,
"statusbarbgremotetheme": "#000000", "statusbarbgremotetheme": "#000000",
"statusbarlighttextremotetheme": true "statusbarlighttextremotetheme": true,
"enableanalytics": false
} }

View File

@ -213,7 +213,7 @@ export class CoreCourseSectionPage implements OnDestroy {
let promise; let promise;
// Add log in Moodle. // 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. // Ignore errors.
}); });

View File

@ -27,6 +27,7 @@ import { CoreConstants } from '../../constants';
import { CoreCourseOfflineProvider } from './course-offline'; import { CoreCourseOfflineProvider } from './course-offline';
import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins'; import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins';
import { CoreCourseFormatDelegate } from './format-delegate'; import { CoreCourseFormatDelegate } from './format-delegate';
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
/** /**
* Service that provides some features regarding a course. * 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 utils: CoreUtilsProvider, private timeUtils: CoreTimeUtilsProvider, private translate: TranslateService,
private courseOffline: CoreCourseOfflineProvider, private appProvider: CoreAppProvider, private courseOffline: CoreCourseOfflineProvider, private appProvider: CoreAppProvider,
private courseFormatDelegate: CoreCourseFormatDelegate, private sitePluginsProvider: CoreSitePluginsProvider, private courseFormatDelegate: CoreCourseFormatDelegate, private sitePluginsProvider: CoreSitePluginsProvider,
private domUtils: CoreDomUtilsProvider) { private domUtils: CoreDomUtilsProvider, protected pushNotificationsProvider: CorePushNotificationsProvider) {
this.logger = logger.getInstance('CoreCourseProvider'); this.logger = logger.getInstance('CoreCourseProvider');
this.sitesProvider.registerSiteSchema(this.siteSchema); this.sitesProvider.registerSiteSchema(this.siteSchema);
@ -811,18 +812,22 @@ export class CoreCourseProvider {
* @param {number} courseId Course ID. * @param {number} courseId Course ID.
* @param {number} [sectionNumber] Section number. * @param {number} [sectionNumber] Section number.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @param {string} [name] Name of the course.
* @return {Promise<void>} Promise resolved when the WS call is successful. * @return {Promise<void>} Promise resolved when the WS call is successful.
*/ */
logView(courseId: number, sectionNumber?: number, siteId?: string): Promise<void> { logView(courseId: number, sectionNumber?: number, siteId?: string, name?: string): Promise<void> {
const params: any = { const params: any = {
courseid: courseId courseid: courseId
}; },
wsName = 'core_course_view_course';
if (typeof sectionNumber != 'undefined') { if (typeof sectionNumber != 'undefined') {
params.sectionnumber = sectionNumber; params.sectionnumber = sectionNumber;
} }
return this.sitesProvider.getSite(siteId).then((site) => { 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) => { return site.write('core_course_view_course', params).then((response) => {
if (!response.status) { if (!response.status) {
return Promise.reject(null); return Promise.reject(null);

View File

@ -36,7 +36,7 @@ export class CoreCourseLogCronHandler implements CoreCronHandler {
*/ */
execute(siteId?: string, force?: boolean): Promise<any> { execute(siteId?: string, force?: boolean): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { 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);
}); });
} }

View File

@ -18,6 +18,7 @@ import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreTimeUtilsProvider } from '@providers/utils/time';
import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreAppProvider } from '@providers/app'; import { CoreAppProvider } from '@providers/app';
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
/** /**
* Helper to manage logging to Moodle. * Helper to manage logging to Moodle.
@ -62,7 +63,7 @@ export class CoreCourseLogHelperProvider {
constructor(protected sitesProvider: CoreSitesProvider, protected timeUtils: CoreTimeUtilsProvider, constructor(protected sitesProvider: CoreSitesProvider, protected timeUtils: CoreTimeUtilsProvider,
protected textUtils: CoreTextUtilsProvider, protected utils: CoreUtilsProvider, protected textUtils: CoreTextUtilsProvider, protected utils: CoreUtilsProvider,
protected appProvider: CoreAppProvider) { protected appProvider: CoreAppProvider, protected pushNotificationsProvider: CorePushNotificationsProvider) {
this.sitesProvider.registerSiteSchema(this.siteSchema); 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<any>} Promise resolved when done.
*/
logSingle(ws: string, data: any, component: string, componentId: number, name?: string, category?: string, eventData?: any,
siteId?: string): Promise<any> {
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<any>} Promise resolved when done.
*/
logList(ws: string, data: any, component: string, componentId: number, category: string, eventData?: any, siteId?: string)
: Promise<any> {
this.pushNotificationsProvider.logViewListEvent(category, ws, eventData, siteId);
return this.log(ws, data, component, componentId, siteId);
}
/** /**
* Save activity log for offline sync. * Save activity log for offline sync.
* *

View File

@ -16,6 +16,7 @@ import { Injectable } from '@angular/core';
import { CoreLoggerProvider } from '@providers/logger'; import { CoreLoggerProvider } from '@providers/logger';
import { CoreSitesProvider } from '@providers/sites'; import { CoreSitesProvider } from '@providers/sites';
import { CoreCoursesProvider } from '@core/courses/providers/courses'; import { CoreCoursesProvider } from '@core/courses/providers/courses';
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
/** /**
* Service to provide grade functionalities. * Service to provide grade functionalities.
@ -33,7 +34,7 @@ export class CoreGradesProvider {
protected logger; protected logger;
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider,
private coursesProvider: CoreCoursesProvider) { private coursesProvider: CoreCoursesProvider, protected pushNotificationsProvider: CorePushNotificationsProvider) {
this.logger = logger.getInstance('CoreGradesProvider'); this.logger = logger.getInstance('CoreGradesProvider');
} }
@ -326,12 +327,25 @@ export class CoreGradesProvider {
* *
* @param {number} courseId Course ID. * @param {number} courseId Course ID.
* @param {number} userId User ID. * @param {number} userId User ID.
* @param {string} [name] Course name. If not set, it will be calculated.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
logCourseGradesView(courseId: number, userId: number): Promise<any> { logCourseGradesView(courseId: number, userId: number, name?: string): Promise<any> {
userId = userId || this.sitesProvider.getCurrentSiteUserId(); 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, courseid: courseId,
userid: userId userid: userId
}); });
@ -348,8 +362,12 @@ export class CoreGradesProvider {
courseId = this.sitesProvider.getCurrentSiteHomeId(); courseId = this.sitesProvider.getCurrentSiteHomeId();
} }
return this.sitesProvider.getCurrentSite().write('gradereport_overview_view_grade_report', { const params = {
courseid: courseId courseid: courseId
}); };
this.pushNotificationsProvider.logViewListEvent('grades', 'gradereport_overview_view_grade_report', params);
return this.sitesProvider.getCurrentSite().write('gradereport_overview_view_grade_report', params);
} }
} }

View File

@ -292,6 +292,86 @@ export class CorePushNotificationsProvider {
return this.getAddonBadge(siteId); 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<any>} Promise resolved when done. This promise is never rejected.
*/
logEvent(name: string, data: any, filter?: boolean): Promise<any> {
const win = <any> 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<any>} Promise resolved when done. This promise is never rejected.
*/
logViewEvent(itemId: number | string, itemName: string, itemCategory: string, wsName: string, data?: any, siteId?: string)
: Promise<any> {
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<any>} Promise resolved when done. This promise is never rejected.
*/
logViewListEvent(itemCategory: string, wsName: string, data?: any, siteId?: string): Promise<any> {
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. * Function called when a push notification is clicked. Redirect the user to the right state.
* *

View File

@ -80,8 +80,18 @@ export class CorePushNotificationsModule {
}); });
// Listen for local notification clicks (generated by the app). // Listen for local notification clicks (generated by the app).
localNotificationsProvider.registerClick(CorePushNotificationsProvider.COMPONENT, localNotificationsProvider.registerClick(CorePushNotificationsProvider.COMPONENT, (notification: any) => {
pushNotificationsProvider.notificationClicked.bind(pushNotificationsProvider)); // 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. // Allow migrating the table from the old app to the new schema.
updateManager.registerAppTableMigration({ updateManager.registerAppTableMigration({

View File

@ -20,6 +20,7 @@ import { CoreCourseHelperProvider } from '@core/course/providers/helper';
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate'; import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
import { CoreBlockDelegate } from '@core/block/providers/delegate'; import { CoreBlockDelegate } from '@core/block/providers/delegate';
import { CoreBlockComponent } from '@core/block/components/block/block'; import { CoreBlockComponent } from '@core/block/components/block/block';
import { CoreSite } from '@classes/site';
/** /**
* Component that displays site home index. * Component that displays site home index.
@ -37,12 +38,14 @@ export class CoreSiteHomeIndexComponent implements OnInit {
hasSupportedBlock: boolean; hasSupportedBlock: boolean;
items: any[] = []; items: any[] = [];
siteHomeId: number; siteHomeId: number;
currentSite: CoreSite;
blocks = []; blocks = [];
constructor(private domUtils: CoreDomUtilsProvider, private sitesProvider: CoreSitesProvider, constructor(private domUtils: CoreDomUtilsProvider, sitesProvider: CoreSitesProvider,
private courseProvider: CoreCourseProvider, private courseHelper: CoreCourseHelperProvider, private courseProvider: CoreCourseProvider, private courseHelper: CoreCourseHelperProvider,
private prefetchDelegate: CoreCourseModulePrefetchDelegate, private blockDelegate: CoreBlockDelegate) { 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. * @param {any} refresher Refresher.
*/ */
doRefresh(refresher: any): void { doRefresh(refresher: any): void {
const promises = [], const promises = [];
currentSite = this.sitesProvider.getCurrentSite();
promises.push(this.courseProvider.invalidateSections(this.siteHomeId)); promises.push(this.courseProvider.invalidateSections(this.siteHomeId));
promises.push(currentSite.invalidateConfig().then(() => { promises.push(this.currentSite.invalidateConfig().then(() => {
// Config invalidated, fetch it again. // Config invalidated, fetch it again.
return currentSite.getConfig().then((config) => { return this.currentSite.getConfig().then((config) => {
currentSite.setConfig(config); this.currentSite.setConfig(config);
}); });
})); }));
@ -102,7 +104,7 @@ export class CoreSiteHomeIndexComponent implements OnInit {
protected loadContent(): Promise<any> { protected loadContent(): Promise<any> {
this.hasContent = false; this.hasContent = false;
const config = this.sitesProvider.getCurrentSite().getStoredConfig() || { numsections: 1 }; const config = this.currentSite.getStoredConfig() || { numsections: 1 };
if (config.frontpageloggedin) { if (config.frontpageloggedin) {
// Items with index 1 and 3 were removed on 2.5 and not being supported in the app. // 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. // 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. // Ignore errors.
}); });

View File

@ -84,7 +84,7 @@ export class CoreUserProfilePage {
*/ */
ionViewDidLoad(): void { ionViewDidLoad(): void {
this.fetchUser().then(() => { 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.isDeleted = error.errorcode === 'userdeleted';
this.isEnrolled = error.errorcode !== 'notenrolledprofile'; this.isEnrolled = error.errorcode !== 'notenrolledprofile';
}); });

View File

@ -20,6 +20,7 @@ import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites';
import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreAppProvider } from '@providers/app'; import { CoreAppProvider } from '@providers/app';
import { CoreUserOfflineProvider } from './offline'; import { CoreUserOfflineProvider } from './offline';
import { CorePushNotificationsProvider } from '@core/pushnotifications/providers/pushnotifications';
/** /**
* Service to provide user functionalities. * Service to provide user functionalities.
@ -63,7 +64,7 @@ export class CoreUserProvider {
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
private filepoolProvider: CoreFilepoolProvider, private appProvider: CoreAppProvider, private filepoolProvider: CoreFilepoolProvider, private appProvider: CoreAppProvider,
private userOffline: CoreUserOfflineProvider) { private userOffline: CoreUserOfflineProvider, private pushNotificationsProvider: CorePushNotificationsProvider) {
this.logger = logger.getInstance('CoreUserProvider'); this.logger = logger.getInstance('CoreUserProvider');
this.sitesProvider.registerSiteSchema(this.siteSchema); this.sitesProvider.registerSiteSchema(this.siteSchema);
} }
@ -432,18 +433,22 @@ export class CoreUserProvider {
* Log User Profile View in Moodle. * Log User Profile View in Moodle.
* @param {number} userId User ID. * @param {number} userId User ID.
* @param {number} [courseId] Course ID. * @param {number} [courseId] Course ID.
* @param {string} [name] Name of the user.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
logView(userId: number, courseId?: number): Promise<any> { logView(userId: number, courseId?: number, name?: string): Promise<any> {
const params = { const params = {
userid: userId userid: userId
}; },
wsName = 'core_user_view_user_profile';
if (courseId) { if (courseId) {
params['courseid'] = 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<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
logParticipantsView(courseId?: number): Promise<any> { logParticipantsView(courseId?: number): Promise<any> {
return this.sitesProvider.getCurrentSite().write('core_user_view_user_list', { const params = {
courseid: courseId courseid: courseId
}); };
this.pushNotificationsProvider.logViewListEvent('user', 'core_user_view_user_list', params);
return this.sitesProvider.getCurrentSite().write('core_user_view_user_list', params);
} }
/** /**

View File

@ -102,6 +102,10 @@ export class CoreLocalNotificationsProvider {
}; };
protected triggerSubscription: Subscription; protected triggerSubscription: Subscription;
protected clickSubscription: 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, constructor(logger: CoreLoggerProvider, private localNotifications: LocalNotifications, private platform: Platform,
private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private configProvider: CoreConfigProvider, private appProvider: CoreAppProvider, private utils: CoreUtilsProvider, private configProvider: CoreConfigProvider,
@ -113,16 +117,31 @@ export class CoreLocalNotificationsProvider {
this.appDB.createTablesFromSchema(this.tablesSchema); this.appDB.createTablesFromSchema(this.tablesSchema);
platform.ready().then(() => { platform.ready().then(() => {
// Listen to events.
this.triggerSubscription = localNotifications.on('trigger').subscribe((notification: ILocalNotification) => { this.triggerSubscription = localNotifications.on('trigger').subscribe((notification: ILocalNotification) => {
this.trigger(notification); this.trigger(notification);
this.handleEvent('trigger', notification);
}); });
this.clickSubscription = localNotifications.on('click').subscribe((notification: ILocalNotification) => { this.clickSubscription = localNotifications.on('click').subscribe((notification: ILocalNotification) => {
if (notification && notification.data) { this.handleEvent('click', notification);
this.logger.debug('Notification clicked: ', notification.data); });
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. // 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. * Returns whether local notifications plugin is installed.
* *
@ -328,12 +361,22 @@ export class CoreLocalNotificationsProvider {
* @param {any} data Data received by the notification. * @param {any} data Data received by the notification.
*/ */
notifyClick(data: any): void { 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. // Execute the code in the Angular zone, so change detection doesn't stop working.
this.zone.run(() => { this.zone.run(() => {
const component = data.component; const component = data.component;
if (component) { if (component) {
if (this.observables[component]) { if (this.observables[eventName] && this.observables[eventName][component]) {
this.observables[component].next(data); 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. * @return {any} Object with an "off" property to stop listening for clicks.
*/ */
registerClick(component: string, callback: Function): any { 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<any>();
} }
this.observables[component].subscribe(callback); /**
* 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] = {};
}
if (typeof this.observables[eventName][component] == 'undefined') {
// No observable for this component, create a new one.
this.observables[eventName][component] = new Subject<any>();
}
this.observables[eventName][component].subscribe(callback);
return { return {
off: (): void => { off: (): void => {
this.observables[component].unsubscribe(callback); this.observables[eventName][component].unsubscribe(callback);
} }
}; };
} }

View File

@ -1049,6 +1049,24 @@ export class CoreUtilsProvider {
return mapped; 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(). * Similar to AngularJS $q.defer().
* *