From 7fa8e6fe057d4a7fb0893033b26b5e72d3c393b7 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 31 Jan 2020 14:53:13 +0100 Subject: [PATCH] MOBILE-3323 editor: Delete draft when form is submitted --- .../calendar/pages/edit-event/edit-event.html | 2 +- .../calendar/pages/edit-event/edit-event.ts | 8 +- .../edit-feedback-modal.html | 2 +- .../edit-feedback-modal.ts | 20 +++- src/addon/mod/assign/pages/edit/edit.html | 2 +- src/addon/mod/assign/pages/edit/edit.ts | 101 ++++++++++-------- src/addon/mod/data/pages/edit/edit.html | 2 +- src/addon/mod/data/pages/edit/edit.ts | 9 +- src/addon/mod/data/pages/search/search.html | 2 +- src/addon/mod/data/pages/search/search.ts | 26 ++++- src/addon/mod/data/providers/data.ts | 45 ++++++-- .../index/addon-mod-lesson-index.html | 2 +- .../mod/lesson/components/index/index.ts | 9 +- .../pages/password-modal/password-modal.html | 2 +- .../pages/password-modal/password-modal.ts | 14 ++- src/addon/mod/lesson/pages/player/player.html | 2 +- src/addon/mod/lesson/pages/player/player.ts | 15 ++- src/addon/mod/lesson/providers/lesson.ts | 3 + src/addon/mod/quiz/pages/player/player.html | 2 +- src/addon/mod/quiz/pages/player/player.ts | 8 +- .../preflight-modal/preflight-modal.html | 2 +- .../pages/preflight-modal/preflight-modal.ts | 21 +++- src/addon/mod/wiki/pages/edit/edit.html | 2 +- src/addon/mod/wiki/pages/edit/edit.ts | 16 ++- ...ddon-mod-workshop-assessment-strategy.html | 2 +- .../assessment-strategy.ts | 12 ++- .../workshop/pages/assessment/assessment.html | 2 +- .../workshop/pages/assessment/assessment.ts | 12 ++- .../edit-submission/edit-submission.html | 2 +- .../pages/edit-submission/edit-submission.ts | 16 ++- .../workshop/pages/submission/submission.html | 2 +- .../workshop/pages/submission/submission.ts | 11 +- src/addon/notes/pages/add/add.html | 2 +- src/addon/notes/pages/add/add.ts | 22 +++- .../local-file/core-local-file.html | 2 +- src/components/local-file/local-file.ts | 26 +++-- src/components/search-box/search-box.ts | 100 +++++++++++++++++ .../core-send-message-form.html | 2 +- .../send-message-form/send-message-form.ts | 17 ++- src/core/comments/pages/add/add.html | 2 +- src/core/comments/pages/add/add.ts | 21 +++- src/core/comments/providers/comments.ts | 2 +- .../self-enrol-password.html | 2 +- .../self-enrol-password.ts | 16 ++- .../rich-text-editor/rich-text-editor.ts | 27 ++++- .../login/pages/credentials/credentials.html | 2 +- .../login/pages/credentials/credentials.ts | 10 +- .../pages/email-signup/email-signup.html | 4 +- .../login/pages/email-signup/email-signup.ts | 33 +++++- .../forgotten-password.html | 2 +- .../forgotten-password/forgotten-password.ts | 22 +++- src/core/login/pages/reconnect/reconnect.html | 2 +- src/core/login/pages/reconnect/reconnect.ts | 23 ++-- src/core/login/pages/site/site.html | 2 +- src/core/login/pages/site/site.ts | 30 +++++- .../search-box/core-search-box.html | 2 +- .../components/search-box/search-box.ts | 12 ++- src/providers/events.ts | 1 + 58 files changed, 608 insertions(+), 154 deletions(-) create mode 100644 src/components/search-box/search-box.ts diff --git a/src/addon/calendar/pages/edit-event/edit-event.html b/src/addon/calendar/pages/edit-event/edit-event.html index 55a2cd584..49c0bb32f 100644 --- a/src/addon/calendar/pages/edit-event/edit-event.html +++ b/src/addon/calendar/pages/edit-event/edit-event.html @@ -9,7 +9,7 @@ -
+

{{ 'addon.calendar.eventname' | translate }}

diff --git a/src/addon/calendar/pages/edit-event/edit-event.ts b/src/addon/calendar/pages/edit-event/edit-event.ts index 16f9350ce..0296859a5 100644 --- a/src/addon/calendar/pages/edit-event/edit-event.ts +++ b/src/addon/calendar/pages/edit-event/edit-event.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy, Optional, ViewChild } from '@angular/core'; +import { Component, OnInit, OnDestroy, Optional, ViewChild, ElementRef } from '@angular/core'; import { FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; @@ -44,6 +44,7 @@ import { CoreFilterHelperProvider } from '@core/filter/providers/helper'; export class AddonCalendarEditEventPage implements OnInit, OnDestroy { @ViewChild(CoreEditorRichTextEditorComponent) descriptionEditor: CoreEditorRichTextEditorComponent; + @ViewChild('editEventForm') formElement: ElementRef; title: string; dateFormat: string; @@ -496,6 +497,11 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy { this.calendarProvider.submitEvent(this.eventId, data).then((result) => { event = result.event; + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: result.sent, + }, this.currentSite.getId()); + if (result.sent) { // Event created or edited, invalidate right days & months. const numberOfRepetitions = formData.repeat ? formData.repeats : diff --git a/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.html b/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.html index a51daa3fd..381d0ff43 100644 --- a/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.html +++ b/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.html @@ -9,7 +9,7 @@ - + diff --git a/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.ts b/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.ts index 41c02baaf..599f8aa86 100644 --- a/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.ts +++ b/src/addon/mod/assign/pages/edit-feedback-modal/edit-feedback-modal.ts @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input } from '@angular/core'; +import { Component, Input, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, ViewController, NavParams } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { AddonModAssignFeedbackDelegate } from '../../providers/feedback-delegate'; import { @@ -36,10 +38,17 @@ export class AddonModAssignEditFeedbackModalPage { @Input() plugin: AddonModAssignPlugin; // The plugin object. @Input() userId: number; // The user ID of the submission. + @ViewChild('editFeedbackForm') formElement: ElementRef; + protected forceLeave = false; // To allow leaving the page without checking for changes. - constructor(params: NavParams, protected viewCtrl: ViewController, protected domUtils: CoreDomUtilsProvider, - protected translate: TranslateService, protected feedbackDelegate: AddonModAssignFeedbackDelegate) { + constructor(params: NavParams, + protected viewCtrl: ViewController, + protected domUtils: CoreDomUtilsProvider, + protected translate: TranslateService, + protected feedbackDelegate: AddonModAssignFeedbackDelegate, + protected eventsProvider: CoreEventsProvider, + protected sitesProvider: CoreSitesProvider) { this.assign = params.get('assign'); this.submission = params.get('submission'); @@ -82,6 +91,11 @@ export class AddonModAssignEditFeedbackModalPage { e.preventDefault(); e.stopPropagation(); + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: false, + }, this.sitesProvider.getCurrentSiteId()); + // Close the modal, sending the input data. this.forceLeave = true; this.closeModal(this.getInputData()); diff --git a/src/addon/mod/assign/pages/edit/edit.html b/src/addon/mod/assign/pages/edit/edit.html index c85acfcf9..f849d0390 100644 --- a/src/addon/mod/assign/pages/edit/edit.html +++ b/src/addon/mod/assign/pages/edit/edit.html @@ -13,7 +13,7 @@ -
+ diff --git a/src/addon/mod/assign/pages/edit/edit.ts b/src/addon/mod/assign/pages/edit/edit.ts index 7eccd3c20..901cc2baa 100644 --- a/src/addon/mod/assign/pages/edit/edit.ts +++ b/src/addon/mod/assign/pages/edit/edit.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '@providers/events'; @@ -34,6 +34,9 @@ import { AddonModAssignHelperProvider } from '../../providers/helper'; templateUrl: 'edit.html', }) export class AddonModAssignEditPage implements OnInit, OnDestroy { + + @ViewChild('editSubmissionForm') formElement: ElementRef; + title: string; // Title to display. assign: AddonModAssignAssign; // Assignment. courseId: number; // Course ID the assignment belongs to. @@ -265,69 +268,77 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy { * * @return Promise resolved when done. */ - protected saveSubmission(): Promise { + protected async saveSubmission(): Promise { const inputData = this.getInputData(); if (this.submissionStatement && (!inputData.submissionstatement || inputData.submissionstatement === 'false')) { - return Promise.reject(this.translate.instant('addon.mod_assign.acceptsubmissionstatement')); + throw this.translate.instant('addon.mod_assign.acceptsubmissionstatement'); } let modal = this.domUtils.showModalLoading(); + let size; // Get size to ask for confirmation. - return this.assignHelper.getSubmissionSizeForEdit(this.assign, this.userSubmission, inputData).catch(() => { + try { + size = await this.assignHelper.getSubmissionSizeForEdit(this.assign, this.userSubmission, inputData); + } catch (error) { // Error calculating size, return -1. - return -1; - }).then((size) => { - modal.dismiss(); + size = -1; + } + modal.dismiss(); + + try { // Confirm action. - return this.fileUploaderHelper.confirmUploadFile(size, true, this.allowOffline); - }).then(() => { + await this.fileUploaderHelper.confirmUploadFile(size, true, this.allowOffline); + modal = this.domUtils.showModalLoading('core.sending', true); - return this.prepareSubmissionData(inputData).then((pluginData) => { - if (!Object.keys(pluginData).length) { - // Nothing to save. - return; - } + const pluginData = await this.prepareSubmissionData(inputData); + if (!Object.keys(pluginData).length) { + // Nothing to save. + return; + } - let promise; + let sent: boolean; - if (this.saveOffline) { - // Save submission in offline. - promise = this.assignOfflineProvider.saveSubmission(this.assign.id, this.courseId, pluginData, - this.userSubmission.timemodified, !this.assign.submissiondrafts, this.userId); - } else { - // Try to send it to server. - promise = this.assignProvider.saveSubmission(this.assign.id, this.courseId, pluginData, this.allowOffline, - this.userSubmission.timemodified, !!this.assign.submissiondrafts, this.userId); - } + if (this.saveOffline) { + // Save submission in offline. + sent = false; + await this.assignOfflineProvider.saveSubmission(this.assign.id, this.courseId, pluginData, + this.userSubmission.timemodified, !this.assign.submissiondrafts, this.userId); + } else { + // Try to send it to server. + sent = await this.assignProvider.saveSubmission(this.assign.id, this.courseId, pluginData, this.allowOffline, + this.userSubmission.timemodified, !!this.assign.submissiondrafts, this.userId); + } - return promise.then(() => { - // Clear temporary data from plugins. - return this.assignHelper.clearSubmissionPluginTmpData(this.assign, this.userSubmission, inputData); - }).then(() => { - // Submission saved, trigger event. - const params = { - assignmentId: this.assign.id, - submissionId: this.userSubmission.id, - userId: this.userId, - }; + // Clear temporary data from plugins. + await this.assignHelper.clearSubmissionPluginTmpData(this.assign, this.userSubmission, inputData); - this.eventsProvider.trigger(AddonModAssignProvider.SUBMISSION_SAVED_EVENT, params, - this.sitesProvider.getCurrentSiteId()); + // Submission saved, trigger events. + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: sent, + }, this.sitesProvider.getCurrentSiteId()); - if (!this.assign.submissiondrafts) { - // No drafts allowed, so it was submitted. Trigger event. - this.eventsProvider.trigger(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, params, - this.sitesProvider.getCurrentSiteId()); - } - }); - }); - }).finally(() => { + const params = { + assignmentId: this.assign.id, + submissionId: this.userSubmission.id, + userId: this.userId, + }; + + this.eventsProvider.trigger(AddonModAssignProvider.SUBMISSION_SAVED_EVENT, params, + this.sitesProvider.getCurrentSiteId()); + + if (!this.assign.submissiondrafts) { + // No drafts allowed, so it was submitted. Trigger event. + this.eventsProvider.trigger(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, params, + this.sitesProvider.getCurrentSiteId()); + } + } finally { modal.dismiss(); - }); + } } /** diff --git a/src/addon/mod/data/pages/edit/edit.html b/src/addon/mod/data/pages/edit/edit.html index d5865e7e3..7b5418da5 100644 --- a/src/addon/mod/data/pages/edit/edit.html +++ b/src/addon/mod/data/pages/edit/edit.html @@ -21,7 +21,7 @@
- +
diff --git a/src/addon/mod/data/pages/edit/edit.ts b/src/addon/mod/data/pages/edit/edit.ts index 38a1bc2e8..6bc8c66ff 100644 --- a/src/addon/mod/data/pages/edit/edit.ts +++ b/src/addon/mod/data/pages/edit/edit.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, ViewChild } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { Content, IonicPage, NavParams, NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { FormGroup } from '@angular/forms'; @@ -40,6 +40,7 @@ import { CoreTagProvider } from '@core/tag/providers/tag'; }) export class AddonModDataEditPage { @ViewChild(Content) content: Content; + @ViewChild('editFormEl') formElement: ElementRef; protected module: any; protected courseId: number; @@ -216,6 +217,12 @@ export class AddonModDataEditPage { // This is done if entry is updated when editing or creating if not. if ((this.entryId && result.updated) || (!this.entryId && result.newentryid)) { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: result.sent, + }, this.siteId); + const promises = []; this.entryId = this.entryId || result.newentryid; diff --git a/src/addon/mod/data/pages/search/search.html b/src/addon/mod/data/pages/search/search.html index 000181e40..6dc36d529 100644 --- a/src/addon/mod/data/pages/search/search.html +++ b/src/addon/mod/data/pages/search/search.html @@ -13,7 +13,7 @@ {{ 'addon.mod_data.search' | translate}} {{ 'addon.mod_data.advancedsearch' | translate }} -
+ diff --git a/src/addon/mod/data/pages/search/search.ts b/src/addon/mod/data/pages/search/search.ts index 5aff12b6b..5834e6ccc 100644 --- a/src/addon/mod/data/pages/search/search.ts +++ b/src/addon/mod/data/pages/search/search.ts @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavParams, ViewController } from 'ionic-angular'; import { FormBuilder, FormGroup } from '@angular/forms'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; @@ -32,6 +34,8 @@ import { CoreTagProvider } from '@core/tag/providers/tag'; templateUrl: 'search.html', }) export class AddonModDataSearchPage { + @ViewChild('searchFormEl') formElement: ElementRef; + search: any; fields: any; data: any; @@ -41,10 +45,17 @@ export class AddonModDataSearchPage { jsData: any; fieldsArray: any; - constructor(params: NavParams, private viewCtrl: ViewController, fb: FormBuilder, protected utils: CoreUtilsProvider, - protected domUtils: CoreDomUtilsProvider, protected fieldsDelegate: AddonModDataFieldsDelegate, - protected textUtils: CoreTextUtilsProvider, protected dataHelper: AddonModDataHelperProvider, - private tagProvider: CoreTagProvider) { + constructor(params: NavParams, + protected viewCtrl: ViewController, + fb: FormBuilder, + protected utils: CoreUtilsProvider, + protected domUtils: CoreDomUtilsProvider, + protected fieldsDelegate: AddonModDataFieldsDelegate, + protected textUtils: CoreTextUtilsProvider, + protected dataHelper: AddonModDataHelperProvider, + protected tagProvider: CoreTagProvider, + protected eventsProvider: CoreEventsProvider, + protected sitesProvider: CoreSitesProvider) { this.search = params.get('search'); this.fields = params.get('fields'); this.data = params.get('data'); @@ -209,6 +220,11 @@ export class AddonModDataSearchPage { this.search.sortBy = searchedData.sortBy; this.search.sortDirection = searchedData.sortDirection; + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: false, + }, this.sitesProvider.getCurrentSiteId()); + this.closeModal(this.search); } } diff --git a/src/addon/mod/data/providers/data.ts b/src/addon/mod/data/providers/data.ts index 0e70b084c..481896b28 100644 --- a/src/addon/mod/data/providers/data.ts +++ b/src/addon/mod/data/providers/data.ts @@ -126,7 +126,8 @@ export class AddonModDataProvider { .then((entry) => { return { // Return provissional entry Id. - newentryid: entry + newentryid: entry, + sent: false, }; }); }; @@ -142,7 +143,11 @@ export class AddonModDataProvider { return storeOffline(); } - return this.addEntryOnline(dataId, contents, groupId, siteId).catch((error) => { + return this.addEntryOnline(dataId, contents, groupId, siteId).then((result) => { + result.sent = true; + + return result; + }).catch((error) => { if (this.utils.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. return Promise.reject(error); @@ -194,7 +199,12 @@ export class AddonModDataProvider { const storeOffline = (): Promise => { const action = approve ? 'approve' : 'disapprove'; - return this.dataOffline.saveEntry(dataId, entryId, action, courseId, undefined, undefined, undefined, siteId); + return this.dataOffline.saveEntry(dataId, entryId, action, courseId, undefined, undefined, undefined, siteId) + .then(() => { + return { + sent: false, + }; + }); }; // Get if the opposite action is not synced. @@ -210,7 +220,11 @@ export class AddonModDataProvider { return storeOffline(); } - return this.approveEntryOnline(entryId, approve, siteId).catch((error) => { + return this.approveEntryOnline(entryId, approve, siteId).then(() => { + return { + sent: true, + }; + }).catch((error) => { if (this.utils.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. return Promise.reject(error); @@ -288,7 +302,12 @@ export class AddonModDataProvider { // Convenience function to store a data to be synchronized later. const storeOffline = (): Promise => { - return this.dataOffline.saveEntry(dataId, entryId, 'delete', courseId, undefined, undefined, undefined, siteId); + return this.dataOffline.saveEntry(dataId, entryId, 'delete', courseId, undefined, undefined, undefined, siteId) + .then(() => { + return { + sent: false, + }; + }); }; let justAdded = false; @@ -318,7 +337,11 @@ export class AddonModDataProvider { return storeOffline(); } - return this.deleteEntryOnline(entryId, siteId).catch((error) => { + return this.deleteEntryOnline(entryId, siteId).then(() => { + return { + sent: true, + }; + }).catch((error) => { if (this.utils.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. return Promise.reject(error); @@ -368,7 +391,8 @@ export class AddonModDataProvider { return this.dataOffline.saveEntry(dataId, entryId, 'edit', courseId, undefined, contents, undefined, siteId) .then(() => { return { - updated: true + updated: true, + sent: false, }; }); }; @@ -408,6 +432,7 @@ export class AddonModDataProvider { return this.addEntry(dataId, entryId, courseId, contents, groupId, fields, siteId, forceOffline) .then((result) => { result.updated = true; + result.sent = true; return result; }); @@ -418,7 +443,11 @@ export class AddonModDataProvider { return storeOffline(); } - return this.editEntryOnline(entryId, contents, siteId).catch((error) => { + return this.editEntryOnline(entryId, contents, siteId).then((result) => { + result.sent = true; + + return result; + }).catch((error) => { if (this.utils.isWebServiceError(error)) { // The WebService has thrown an error, this means that responses cannot be submitted. return Promise.reject(error); diff --git a/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html b/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html index 440d7f383..f7ac672e0 100644 --- a/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html +++ b/src/addon/mod/lesson/components/index/addon-mod-lesson-index.html @@ -34,7 +34,7 @@ - + {{ 'addon.mod_lesson.enterpassword' | translate }} diff --git a/src/addon/mod/lesson/components/index/index.ts b/src/addon/mod/lesson/components/index/index.ts index 17dec12ee..b3b4b95a7 100644 --- a/src/addon/mod/lesson/components/index/index.ts +++ b/src/addon/mod/lesson/components/index/index.ts @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Optional, Injector, Input, ViewChild } from '@angular/core'; +import { Component, Optional, Injector, Input, ViewChild, ElementRef } from '@angular/core'; import { Content, NavController } from 'ionic-angular'; +import { CoreEventsProvider } from '@providers/events'; import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreUtilsProvider } from '@providers/utils/utils'; @@ -35,6 +36,7 @@ import { CoreTabsComponent } from '@components/tabs/tabs'; }) export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityComponent { @ViewChild(CoreTabsComponent) tabsComponent: CoreTabsComponent; + @ViewChild('passwordForm') formElement: ElementRef; @Input() group: number; // The group to display. @Input() action: string; // The "action" to display first. @@ -584,6 +586,11 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo this.loaded = true; this.refreshIcon = 'refresh'; this.syncIcon = 'sync'; + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }, this.siteId); }); } diff --git a/src/addon/mod/lesson/pages/password-modal/password-modal.html b/src/addon/mod/lesson/pages/password-modal/password-modal.html index b673d540c..3c35a3dcc 100644 --- a/src/addon/mod/lesson/pages/password-modal/password-modal.html +++ b/src/addon/mod/lesson/pages/password-modal/password-modal.html @@ -9,7 +9,7 @@ - + {{ 'addon.mod_lesson.enterpassword' | translate }} diff --git a/src/addon/mod/lesson/pages/password-modal/password-modal.ts b/src/addon/mod/lesson/pages/password-modal/password-modal.ts index 13fbfcdb1..d72df1ca4 100644 --- a/src/addon/mod/lesson/pages/password-modal/password-modal.ts +++ b/src/addon/mod/lesson/pages/password-modal/password-modal.ts @@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, ViewController } from 'ionic-angular'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; /** * Modal that asks the password for a lesson. @@ -24,8 +26,11 @@ import { IonicPage, ViewController } from 'ionic-angular'; templateUrl: 'password-modal.html', }) export class AddonModLessonPasswordModalPage { + @ViewChild('passwordForm') formElement: ElementRef; - constructor(protected viewCtrl: ViewController) { } + constructor(protected viewCtrl: ViewController, + protected eventsProvider: CoreEventsProvider, + protected sitesProvider: CoreSitesProvider) { } /** * Send the password back. @@ -37,6 +42,11 @@ export class AddonModLessonPasswordModalPage { e.preventDefault(); e.stopPropagation(); + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: false, + }, this.sitesProvider.getCurrentSiteId()); + this.viewCtrl.dismiss(password.value); } diff --git a/src/addon/mod/lesson/pages/player/player.html b/src/addon/mod/lesson/pages/player/player.html index b8526c401..dfe4c3e86 100644 --- a/src/addon/mod/lesson/pages/player/player.html +++ b/src/addon/mod/lesson/pages/player/player.html @@ -38,7 +38,7 @@ - + diff --git a/src/addon/mod/lesson/pages/player/player.ts b/src/addon/mod/lesson/pages/player/player.ts index c29ad99b5..7ab23e96f 100644 --- a/src/addon/mod/lesson/pages/player/player.ts +++ b/src/addon/mod/lesson/pages/player/player.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef, ElementRef } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { IonicPage, NavParams, Content, PopoverController, ModalController, Modal, NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; @@ -41,6 +41,7 @@ import { AddonModLessonHelperProvider } from '../../providers/helper'; }) export class AddonModLessonPlayerPage implements OnInit, OnDestroy { @ViewChild(Content) content: Content; + @ViewChild('questionFormEl') formElement: ElementRef; component = AddonModLessonProvider.COMPONENT; LESSON_EOL = AddonModLessonProvider.LESSON_EOL; @@ -540,15 +541,23 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { * Process a page, sending some data. * * @param data The data to send. + * @param formSubmitted Whether a form was submitted. * @return Promise resolved when done. */ - protected processPage(data: any): Promise { + protected processPage(data: any, formSubmitted?: boolean): Promise { this.loaded = false; const args = [this.lesson, this.courseId, this.pageData, data, this.password, this.review, this.offline, this.accessInfo, this.jumps]; return this.callFunction(this.lessonProvider.processPage.bind(this.lessonProvider), args, 6, 8).then((result) => { + if (formSubmitted) { + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: result.sent, + }, this.sitesProvider.getCurrentSiteId()); + } + if (!this.offline && !this.review && this.lessonProvider.isLessonOffline(this.lesson)) { // Lesson allows offline and the user changed some data in server. Update cached data. const retake = this.accessInfo.attemptscount; @@ -637,7 +646,7 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { // Use getRawValue to include disabled values. const data = this.lessonHelper.prepareQuestionData(this.question, this.questionForm.getRawValue()); - this.processPage(data).finally(() => { + this.processPage(data, true).finally(() => { this.loaded = true; }); } diff --git a/src/addon/mod/lesson/providers/lesson.ts b/src/addon/mod/lesson/providers/lesson.ts index b7fabe1f1..a4db30c6f 100644 --- a/src/addon/mod/lesson/providers/lesson.ts +++ b/src/addon/mod/lesson/providers/lesson.ts @@ -3089,6 +3089,7 @@ export class AddonModLessonProvider { result.warnings = []; result.displaymenu = pageData.displaymenu; // Keep the same value since we can't calculate it in offline. result.messages = this.getPageProcessMessages(lesson, accessInfo, result, review, jumps); + result.sent = false; Object.assign(result, calculatedData); return result; @@ -3104,6 +3105,8 @@ export class AddonModLessonProvider { review: review }, this.sitesProvider.getCurrentSiteId()); + response.sent = true; + return response; }); } diff --git a/src/addon/mod/quiz/pages/player/player.html b/src/addon/mod/quiz/pages/player/player.html index e632829c1..81d271d7d 100644 --- a/src/addon/mod/quiz/pages/player/player.html +++ b/src/addon/mod/quiz/pages/player/player.html @@ -45,7 +45,7 @@ - +
diff --git a/src/addon/mod/quiz/pages/player/player.ts b/src/addon/mod/quiz/pages/player/player.ts index 81f7989d5..31acddaa4 100644 --- a/src/addon/mod/quiz/pages/player/player.ts +++ b/src/addon/mod/quiz/pages/player/player.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef, ViewChildren, QueryList } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef, ViewChildren, QueryList, ElementRef } from '@angular/core'; import { IonicPage, NavParams, Content, PopoverController, ModalController, Modal, NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '@providers/events'; @@ -41,6 +41,7 @@ import { Subscription } from 'rxjs'; export class AddonModQuizPlayerPage implements OnInit, OnDestroy { @ViewChild(Content) content: Content; @ViewChildren(CoreQuestionComponent) questionComponents: QueryList; + @ViewChild('quizForm') formElement: ElementRef; quiz: any; // The quiz the attempt belongs to. attempt: any; // The attempt being attempted. @@ -585,6 +586,11 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy { // Answers saved, cancel auto save. this.autoSave.cancelAutoSave(); this.autoSave.hideAutoSaveError(); + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: !this.offline, + }, this.sitesProvider.getCurrentSiteId()); }); } diff --git a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.html b/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.html index c00c08277..69d32d7ab 100644 --- a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.html +++ b/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.html @@ -10,7 +10,7 @@ - + diff --git a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.ts b/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.ts index 590d03137..28681c8a5 100644 --- a/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.ts +++ b/src/addon/mod/quiz/pages/preflight-modal/preflight-modal.ts @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, Injector, ViewChild } from '@angular/core'; +import { Component, OnInit, Injector, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, ViewController, NavParams, Content } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { FormBuilder, FormGroup } from '@angular/forms'; +import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-delegate'; @@ -31,6 +32,7 @@ import { AddonModQuizAccessRuleDelegate } from '../../providers/access-rules-del export class AddonModQuizPreflightModalPage implements OnInit { @ViewChild(Content) content: Content; + @ViewChild('preflightFormEl') formElement: ElementRef; preflightForm: FormGroup; title: string; @@ -43,9 +45,15 @@ export class AddonModQuizPreflightModalPage implements OnInit { protected siteId: string; protected rules: string[]; - constructor(params: NavParams, fb: FormBuilder, translate: TranslateService, sitesProvider: CoreSitesProvider, - protected viewCtrl: ViewController, protected accessRuleDelegate: AddonModQuizAccessRuleDelegate, - protected injector: Injector, protected domUtils: CoreDomUtilsProvider) { + constructor(params: NavParams, + fb: FormBuilder, + translate: TranslateService, + sitesProvider: CoreSitesProvider, + protected viewCtrl: ViewController, + protected accessRuleDelegate: AddonModQuizAccessRuleDelegate, + protected injector: Injector, + protected domUtils: CoreDomUtilsProvider, + protected eventsProvider: CoreEventsProvider) { this.title = params.get('title') || translate.instant('addon.mod_quiz.startattempt'); this.quiz = params.get('quiz'); @@ -112,6 +120,11 @@ export class AddonModQuizPreflightModalPage implements OnInit { this.domUtils.showErrorModal('core.errorinvalidform', true); } } else { + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: false, + }, this.siteId); + this.viewCtrl.dismiss(this.preflightForm.value); } } diff --git a/src/addon/mod/wiki/pages/edit/edit.html b/src/addon/mod/wiki/pages/edit/edit.html index 2381df6c3..aa2491c90 100644 --- a/src/addon/mod/wiki/pages/edit/edit.html +++ b/src/addon/mod/wiki/pages/edit/edit.html @@ -11,7 +11,7 @@ - + diff --git a/src/addon/mod/wiki/pages/edit/edit.ts b/src/addon/mod/wiki/pages/edit/edit.ts index 033ec982e..7df9ad26a 100644 --- a/src/addon/mod/wiki/pages/edit/edit.ts +++ b/src/addon/mod/wiki/pages/edit/edit.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { FormControl, FormGroup, FormBuilder } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; @@ -37,6 +37,8 @@ import { AddonModWikiSyncProvider, AddonModWikiSyncSubwikiResult } from '../../p }) export class AddonModWikiEditPage implements OnInit, OnDestroy { + @ViewChild('editPageForm') formElement: ElementRef; + title: string; // Title to display. pageForm: FormGroup; // The form group. contentControl: FormControl; // The FormControl for the page content. @@ -423,6 +425,12 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy { if (this.editing) { // Edit existing page. promise = this.wikiProvider.editPage(this.pageId, text, this.section).then(() => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }, this.sitesProvider.getCurrentSiteId()); + // Invalidate page since it changed. return this.wikiProvider.invalidatePage(this.pageId).then(() => { return this.gotoPage(title); @@ -456,6 +464,12 @@ export class AddonModWikiEditPage implements OnInit, OnDestroy { let wikiId = this.wikiId || (this.module && this.module.instance); return this.wikiProvider.newPage(title, text, this.subwikiId, wikiId, this.userId, this.groupId).then((id) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: id > 0, + }, this.sitesProvider.getCurrentSiteId()); + if (id > 0) { // Page was created, get its data and go to the page. this.pageId = id; diff --git a/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html b/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html index b79857e1d..c3bb1fd41 100644 --- a/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html +++ b/src/addon/mod/workshop/components/assessment-strategy/addon-mod-workshop-assessment-strategy.html @@ -1,6 +1,6 @@

{{ 'addon.mod_workshop.assessmentform' | translate }}

- + diff --git a/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts b/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts index e92c1e998..9b239ddac 100644 --- a/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts +++ b/src/addon/mod/workshop/components/assessment-strategy/assessment-strategy.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, OnInit, Injector } from '@angular/core'; +import { Component, Input, OnInit, Injector, ViewChild, ElementRef } from '@angular/core'; import { FormControl } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { CoreSyncProvider } from '@providers/sync'; @@ -44,6 +44,8 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit { @Input() strategy: string; @Input() edit?: boolean; + @ViewChild('assessmentForm') formElement: ElementRef; + componentClass: any; data = { workshopId: 0, @@ -292,7 +294,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit { // Save assessment in offline. return this.workshopOffline.saveAssessment(this.workshop.id, this.assessmentId, this.workshop.course, assessmentData).then(() => { - // Don't return anything. + return false; }); } @@ -301,6 +303,12 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit { return this.workshopProvider.updateAssessment(this.workshop.id, this.assessmentId, this.workshop.course, assessmentData, false, allowOffline); }).then((grade) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: !!grade, + }, this.sitesProvider.getCurrentSiteId()); + const promises = []; // If sent to the server, invalidate and clean. diff --git a/src/addon/mod/workshop/pages/assessment/assessment.html b/src/addon/mod/workshop/pages/assessment/assessment.html index 6d093a0c4..cfb72fa00 100644 --- a/src/addon/mod/workshop/pages/assessment/assessment.html +++ b/src/addon/mod/workshop/pages/assessment/assessment.html @@ -38,7 +38,7 @@ - +

{{ 'addon.mod_workshop.assessmentsettings' | translate }}

diff --git a/src/addon/mod/workshop/pages/assessment/assessment.ts b/src/addon/mod/workshop/pages/assessment/assessment.ts index 1fe9fe5aa..ae9a53d62 100644 --- a/src/addon/mod/workshop/pages/assessment/assessment.ts +++ b/src/addon/mod/workshop/pages/assessment/assessment.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavParams, NavController } from 'ionic-angular'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; @@ -39,6 +39,8 @@ import { AddonModWorkshopSyncProvider } from '../../providers/sync'; }) export class AddonModWorkshopAssessmentPage implements OnInit, OnDestroy { + @ViewChild('evaluateFormEl') formElement: ElementRef; + assessment: any; submission: any; profile: any; @@ -340,7 +342,13 @@ export class AddonModWorkshopAssessmentPage implements OnInit, OnDestroy { // Try to send it to server. return this.workshopProvider.evaluateAssessment(this.workshopId, this.assessmentId, this.courseId, inputData.text, - inputData.weight, inputData.grade).then(() => { + inputData.weight, inputData.grade).then((result) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: !!result, + }, this.siteId); + const data = { workshopId: this.workshopId, assessmentId: this.assessmentId, diff --git a/src/addon/mod/workshop/pages/edit-submission/edit-submission.html b/src/addon/mod/workshop/pages/edit-submission/edit-submission.html index a7f3bad68..99e4d0bf3 100644 --- a/src/addon/mod/workshop/pages/edit-submission/edit-submission.html +++ b/src/addon/mod/workshop/pages/edit-submission/edit-submission.html @@ -10,7 +10,7 @@ - + {{ 'addon.mod_workshop.submissiontitle' | translate }} diff --git a/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts b/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts index c3fb1edbb..121d11ed8 100644 --- a/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts +++ b/src/addon/mod/workshop/pages/edit-submission/edit-submission.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavParams, NavController } from 'ionic-angular'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; @@ -37,6 +37,8 @@ import { AddonModWorkshopOfflineProvider } from '../../providers/offline'; }) export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { + @ViewChild('editFormEl') formElement: ElementRef; + module: any; courseId: number; access: any; @@ -352,7 +354,7 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { // Save submission in offline. return this.workshopOffline.saveSubmission(this.workshopId, this.courseId, inputData.title, inputData.content, attachmentsId, submissionId, 'update').then(() => { - // Don't return anything. + return false; }); } @@ -365,8 +367,8 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { if (saveOffline) { // Save submission in offline. return this.workshopOffline.saveSubmission(this.workshopId, this.courseId, inputData.title, inputData.content, - attachmentsId, submissionId, 'add').then(() => { - // Don't return anything. + attachmentsId, submissionId, 'add').then(() => { + return false; }); } @@ -375,6 +377,12 @@ export class AddonModWorkshopEditSubmissionPage implements OnInit, OnDestroy { return this.workshopProvider.addSubmission(this.workshopId, this.courseId, inputData.title, inputData.content, attachmentsId, undefined, submissionId, allowOffline); }).then((newSubmissionId) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: !!newSubmissionId, + }, this.siteId); + const data = { workshopId: this.workshopId, cmId: this.module.cmid diff --git a/src/addon/mod/workshop/pages/submission/submission.html b/src/addon/mod/workshop/pages/submission/submission.html index 0951709ae..5bff46089 100644 --- a/src/addon/mod/workshop/pages/submission/submission.html +++ b/src/addon/mod/workshop/pages/submission/submission.html @@ -65,7 +65,7 @@ - +

{{ 'addon.mod_workshop.feedbackauthor' | translate }}

diff --git a/src/addon/mod/workshop/pages/submission/submission.ts b/src/addon/mod/workshop/pages/submission/submission.ts index 3fb884e5f..a1c3b6d50 100644 --- a/src/addon/mod/workshop/pages/submission/submission.ts +++ b/src/addon/mod/workshop/pages/submission/submission.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit, OnDestroy, Optional, ViewChild } from '@angular/core'; +import { Component, OnInit, OnDestroy, Optional, ViewChild, ElementRef } from '@angular/core'; import { Content, IonicPage, NavParams, NavController } from 'ionic-angular'; import { FormGroup, FormBuilder } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; @@ -41,6 +41,7 @@ import { AddonModWorkshopSyncProvider } from '../../providers/sync'; export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy { @ViewChild(AddonModWorkshopAssessmentStrategyComponent) assessmentStrategy: AddonModWorkshopAssessmentStrategyComponent; + @ViewChild('feedbackFormEl') formElement: ElementRef; module: any; workshop: any; @@ -444,7 +445,13 @@ export class AddonModWorkshopSubmissionPage implements OnInit, OnDestroy { // Try to send it to server. return this.workshopProvider.evaluateSubmission(this.workshopId, this.submissionId, this.courseId, inputData.text, - inputData.published, inputData.grade).then(() => { + inputData.published, inputData.grade).then((result) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: !!result, + }, this.siteId); + const data = { workshopId: this.workshopId, cmId: this.module.cmid, diff --git a/src/addon/notes/pages/add/add.html b/src/addon/notes/pages/add/add.html index b6df1d660..93f2a04a4 100644 --- a/src/addon/notes/pages/add/add.html +++ b/src/addon/notes/pages/add/add.html @@ -9,7 +9,7 @@ - + {{ 'addon.notes.publishstate' | translate }} diff --git a/src/addon/notes/pages/add/add.ts b/src/addon/notes/pages/add/add.ts index 15fd9da0a..d00e95aa2 100644 --- a/src/addon/notes/pages/add/add.ts +++ b/src/addon/notes/pages/add/add.ts @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, ViewController, NavParams } from 'ionic-angular'; import { CoreAppProvider } from '@providers/app'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { AddonNotesProvider } from '../../providers/notes'; @@ -27,14 +29,22 @@ import { AddonNotesProvider } from '../../providers/notes'; templateUrl: 'add.html', }) export class AddonNotesAddPage { + + @ViewChild('itemEdit') formElement: ElementRef; + userId: number; courseId: number; type = 'personal'; text = ''; processing = false; - constructor(params: NavParams, private viewCtrl: ViewController, private appProvider: CoreAppProvider, - private domUtils: CoreDomUtilsProvider, private notesProvider: AddonNotesProvider) { + constructor(params: NavParams, + protected viewCtrl: ViewController, + protected appProvider: CoreAppProvider, + protected domUtils: CoreDomUtilsProvider, + protected notesProvider: AddonNotesProvider, + protected eventsProvider: CoreEventsProvider, + protected sitesProvider: CoreSitesProvider) { this.userId = params.get('userId'); this.courseId = params.get('courseId'); this.type = params.get('type') || 'personal'; @@ -54,6 +64,12 @@ export class AddonNotesAddPage { // Freeze the add note button. this.processing = true; this.notesProvider.addNote(this.userId, this.courseId, this.type, this.text).then((sent) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: sent, + }, this.sitesProvider.getCurrentSiteId()); + this.viewCtrl.dismiss({type: this.type, sent: true}).finally(() => { this.domUtils.showToast(sent ? 'addon.notes.eventnotecreated' : 'core.datastoredoffline', true, 3000); }); diff --git a/src/components/local-file/core-local-file.html b/src/components/local-file/core-local-file.html index a988397a9..ffec36361 100644 --- a/src/components/local-file/core-local-file.html +++ b/src/components/local-file/core-local-file.html @@ -1,4 +1,4 @@ - + {{fileExtension}} diff --git a/src/components/local-file/local-file.ts b/src/components/local-file/local-file.ts index e1c46b1c2..31fd41bf0 100644 --- a/src/components/local-file/local-file.ts +++ b/src/components/local-file/local-file.ts @@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, Input, Output, OnInit, EventEmitter } from '@angular/core'; +import { Component, Input, Output, OnInit, EventEmitter, ViewChild, ElementRef } from '@angular/core'; +import { CoreEventsProvider } from '@providers/events'; import { CoreFileProvider } from '@providers/file'; +import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreMimetypeUtilsProvider } from '@providers/utils/mimetype'; import { CoreTextUtilsProvider } from '@providers/utils/text'; @@ -38,6 +40,8 @@ export class CoreLocalFileComponent implements OnInit { @Output() onRename?: EventEmitter; // Will notify when the file is renamed. Receives the FileEntry as the param. @Output() onClick?: EventEmitter; // Will notify when the file is clicked. Only if overrideClick is true. + @ViewChild('nameForm') formElement: ElementRef; + fileName: string; fileIcon: string; fileExtension: string; @@ -47,12 +51,14 @@ export class CoreLocalFileComponent implements OnInit { editMode: boolean; relativePath: string; - constructor(private mimeUtils: CoreMimetypeUtilsProvider, - private utils: CoreUtilsProvider, - private textUtils: CoreTextUtilsProvider, - private fileProvider: CoreFileProvider, - private domUtils: CoreDomUtilsProvider, - private timeUtils: CoreTimeUtilsProvider) { + constructor(protected mimeUtils: CoreMimetypeUtilsProvider, + protected utils: CoreUtilsProvider, + protected textUtils: CoreTextUtilsProvider, + protected fileProvider: CoreFileProvider, + protected domUtils: CoreDomUtilsProvider, + protected timeUtils: CoreTimeUtilsProvider, + protected sitesProvider: CoreSitesProvider, + protected eventsProvider: CoreEventsProvider) { this.onDelete = new EventEmitter(); this.onRename = new EventEmitter(); this.onClick = new EventEmitter(); @@ -152,6 +158,12 @@ export class CoreLocalFileComponent implements OnInit { }).catch(() => { // File doesn't exist, move it. return this.fileProvider.moveFile(this.relativePath, newPath).then((fileEntry) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: false, + }, this.sitesProvider.getCurrentSiteId()); + this.editMode = false; this.file = fileEntry; this.loadFileBasicData(); diff --git a/src/components/search-box/search-box.ts b/src/components/search-box/search-box.ts new file mode 100644 index 000000000..4698e4584 --- /dev/null +++ b/src/components/search-box/search-box.ts @@ -0,0 +1,100 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Component, Input, Output, EventEmitter, OnInit, ViewChild, ElementRef } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; +import { CoreUtilsProvider } from '@providers/utils/utils'; + +/** + * Component to display a "search box". + * + * @description + * This component will display a standalone search box with its search button in order to have a better UX. + * + * Example usage: + * + */ +@Component({ + selector: 'core-search-box', + templateUrl: 'core-search-box.html' +}) +export class CoreSearchBoxComponent implements OnInit { + @Input() searchLabel?: string; // Label to be used on action button. + @Input() placeholder?: string; // Placeholder text for search text input. + @Input() autocorrect = 'on'; // Enables/disable Autocorrection on search text input. + @Input() spellcheck?: string | boolean = true; // Enables/disable Spellchecker on search text input. + @Input() autoFocus?: string | boolean; // Enables/disable Autofocus when entering view. + @Input() lengthCheck = 3; // Check value length before submit. If 0, any string will be submitted. + @Input() showClear = true; // Show/hide clear button. + @Input() disabled = false; // Disables the input text. + @Input() initialSearch: string; // Initial search text. + @Output() onSubmit: EventEmitter; // Send data when submitting the search form. + @Output() onClear: EventEmitter; // Send event when clearing the search form. + + @ViewChild('searchForm') formElement: ElementRef; + + searched = false; + searchText = ''; + + constructor(protected translate: TranslateService, + protected utils: CoreUtilsProvider, + protected eventsProvider: CoreEventsProvider, + protected sitesProvider: CoreSitesProvider) { + this.onSubmit = new EventEmitter(); + this.onClear = new EventEmitter(); + } + + ngOnInit(): void { + this.searchLabel = this.searchLabel || this.translate.instant('core.search'); + this.placeholder = this.placeholder || this.translate.instant('core.search'); + this.spellcheck = this.utils.isTrueOrOne(this.spellcheck); + this.showClear = this.utils.isTrueOrOne(this.showClear); + this.searchText = this.initialSearch || ''; + } + + /** + * Form submitted. + * + * @param e Event. + */ + submitForm(e: Event): void { + e.preventDefault(); + e.stopPropagation(); + + if (this.searchText.length < this.lengthCheck) { + // The view should handle this case, but we check it here too just in case. + return; + } + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: false, + }, this.sitesProvider.getCurrentSiteId()); + + this.searched = true; + this.onSubmit.emit(this.searchText); + } + + /** + * Form submitted. + */ + clearForm(): void { + this.searched = false; + this.searchText = ''; + this.onClear.emit(); + } +} diff --git a/src/components/send-message-form/core-send-message-form.html b/src/components/send-message-form/core-send-message-form.html index 8caa886c3..bed8756ce 100644 --- a/src/components/send-message-form/core-send-message-form.html +++ b/src/components/send-message-form/core-send-message-form.html @@ -1,4 +1,4 @@ - +
- + diff --git a/src/core/login/pages/credentials/credentials.ts b/src/core/login/pages/credentials/credentials.ts index 13b81d12c..3d3f6f5e4 100644 --- a/src/core/login/pages/credentials/credentials.ts +++ b/src/core/login/pages/credentials/credentials.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreAppProvider } from '@providers/app'; @@ -32,6 +32,9 @@ import { CoreConfigConstants } from '../../../../configconstants'; templateUrl: 'credentials.html', }) export class CoreLoginCredentialsPage { + + @ViewChild('credentialsForm') formElement: ElementRef; + credForm: FormGroup; siteUrl: string; siteChecked = false; @@ -242,6 +245,11 @@ export class CoreLoginCredentialsPage { } }).finally(() => { modal.dismiss(); + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }); }); } diff --git a/src/core/login/pages/email-signup/email-signup.html b/src/core/login/pages/email-signup/email-signup.html index 2e49ea822..3e52b4b6c 100644 --- a/src/core/login/pages/email-signup/email-signup.html +++ b/src/core/login/pages/email-signup/email-signup.html @@ -17,7 +17,7 @@ - +

{{ 'core.agelocationverification' | translate }}

@@ -47,7 +47,7 @@ -
+

{{siteUrl}}

diff --git a/src/core/login/pages/email-signup/email-signup.ts b/src/core/login/pages/email-signup/email-signup.ts index ffb8a6f42..e9563da59 100644 --- a/src/core/login/pages/email-signup/email-signup.ts +++ b/src/core/login/pages/email-signup/email-signup.ts @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, ViewChild } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, NavParams, Content } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; +import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; @@ -35,6 +36,8 @@ import { CoreConfigConstants } from '../../../../configconstants'; }) export class CoreLoginEmailSignupPage { @ViewChild(Content) content: Content; + @ViewChild('ageForm') ageFormElement: ElementRef; + @ViewChild('signupFormEl') signupFormElement: ElementRef; signupForm: FormGroup; siteUrl: string; @@ -66,10 +69,18 @@ export class CoreLoginEmailSignupPage { policyErrors: any; namefieldsErrors: any; - constructor(private navCtrl: NavController, navParams: NavParams, private fb: FormBuilder, private wsProvider: CoreWSProvider, - private sitesProvider: CoreSitesProvider, private loginHelper: CoreLoginHelperProvider, - private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private utils: CoreUtilsProvider, - private textUtils: CoreTextUtilsProvider, private userProfileFieldDelegate: CoreUserProfileFieldDelegate) { + constructor(protected navCtrl: NavController, + navParams: NavParams, + protected fb: FormBuilder, + protected wsProvider: CoreWSProvider, + protected sitesProvider: CoreSitesProvider, + protected loginHelper: CoreLoginHelperProvider, + protected domUtils: CoreDomUtilsProvider, + protected translate: TranslateService, + protected utils: CoreUtilsProvider, + protected textUtils: CoreTextUtilsProvider, + protected userProfileFieldDelegate: CoreUserProfileFieldDelegate, + protected eventsProvider: CoreEventsProvider) { this.siteUrl = navParams.get('siteUrl'); @@ -265,6 +276,12 @@ export class CoreLoginEmailSignupPage { return this.wsProvider.callAjax('auth_email_signup_user', params, { siteUrl: this.siteUrl }); }).then((result) => { if (result.success) { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.signupFormElement.nativeElement, + online: true, + }); + // Show alert and ho back. const message = this.translate.instant('core.login.emailconfirmsent', { $a: params.email }); this.domUtils.showAlert(this.translate.instant('core.success'), message); @@ -334,6 +351,12 @@ export class CoreLoginEmailSignupPage { params.age = parseInt(params.age, 10); // Use just the integer part. this.wsProvider.callAjax('core_auth_is_minor', params, {siteUrl: this.siteUrl}).then((result) => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.ageFormElement.nativeElement, + online: true, + }); + if (!result.status) { if (this.countryControl.value) { this.signUpCountryControl.setValue(this.countryControl.value); diff --git a/src/core/login/pages/forgotten-password/forgotten-password.html b/src/core/login/pages/forgotten-password/forgotten-password.html index 182ef2312..81905e4d4 100644 --- a/src/core/login/pages/forgotten-password/forgotten-password.html +++ b/src/core/login/pages/forgotten-password/forgotten-password.html @@ -10,7 +10,7 @@
- + {{ 'core.login.searchby' | translate }} diff --git a/src/core/login/pages/forgotten-password/forgotten-password.ts b/src/core/login/pages/forgotten-password/forgotten-password.ts index b1d5d6785..1187d903a 100644 --- a/src/core/login/pages/forgotten-password/forgotten-password.ts +++ b/src/core/login/pages/forgotten-password/forgotten-password.ts @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreLoginHelperProvider } from '../../providers/helper'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; @@ -28,11 +30,20 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; templateUrl: 'forgotten-password.html', }) export class CoreLoginForgottenPasswordPage { + + @ViewChild('resetPasswordForm') formElement: ElementRef; + myForm: FormGroup; siteUrl: string; - constructor(private navCtrl: NavController, navParams: NavParams, fb: FormBuilder, private translate: TranslateService, - private loginHelper: CoreLoginHelperProvider, private domUtils: CoreDomUtilsProvider) { + constructor(protected navCtrl: NavController, + navParams: NavParams, + fb: FormBuilder, + protected translate: TranslateService, + protected loginHelper: CoreLoginHelperProvider, + protected domUtils: CoreDomUtilsProvider, + protected eventsProvider: CoreEventsProvider, + protected sitesProvider: CoreSitesProvider) { this.siteUrl = navParams.get('siteUrl'); this.myForm = fb.group({ @@ -71,6 +82,11 @@ export class CoreLoginForgottenPasswordPage { this.domUtils.showErrorModal(response.notice); } else { // Success. + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }); + this.domUtils.showAlert(this.translate.instant('core.success'), response.notice); this.navCtrl.pop(); } diff --git a/src/core/login/pages/reconnect/reconnect.html b/src/core/login/pages/reconnect/reconnect.html index bfb460784..1b87186ee 100644 --- a/src/core/login/pages/reconnect/reconnect.html +++ b/src/core/login/pages/reconnect/reconnect.html @@ -29,7 +29,7 @@ {{ 'core.login.reconnectdescription' | translate }}

- +

{{username}}

diff --git a/src/core/login/pages/reconnect/reconnect.ts b/src/core/login/pages/reconnect/reconnect.ts index 9372ed2b7..73b551574 100644 --- a/src/core/login/pages/reconnect/reconnect.ts +++ b/src/core/login/pages/reconnect/reconnect.ts @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { CoreAppProvider } from '@providers/app'; +import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreLoginHelperProvider } from '../../providers/helper'; @@ -29,6 +30,9 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; templateUrl: 'reconnect.html', }) export class CoreLoginReconnectPage { + + @ViewChild('reconnectForm') formElement: ElementRef; + credForm: FormGroup; siteUrl: string; username: string; @@ -47,13 +51,14 @@ export class CoreLoginReconnectPage { protected isLoggedOut: boolean; protected siteId: string; - constructor(private navCtrl: NavController, + constructor(protected navCtrl: NavController, navParams: NavParams, fb: FormBuilder, - private appProvider: CoreAppProvider, - private sitesProvider: CoreSitesProvider, - private loginHelper: CoreLoginHelperProvider, - private domUtils: CoreDomUtilsProvider) { + protected appProvider: CoreAppProvider, + protected sitesProvider: CoreSitesProvider, + protected loginHelper: CoreLoginHelperProvider, + protected domUtils: CoreDomUtilsProvider, + protected eventsProvider: CoreEventsProvider) { const currentSite = this.sitesProvider.getCurrentSite(); @@ -175,6 +180,12 @@ export class CoreLoginReconnectPage { // Start the authentication process. this.sitesProvider.getUserToken(siteUrl, username, password).then((data) => { return this.sitesProvider.updateSiteToken(this.infoSiteUrl, username, data.token, data.privateToken).then(() => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }); + // Update site info too because functions might have changed (e.g. unisntall local_mobile). return this.sitesProvider.updateSiteInfoByUrl(this.infoSiteUrl, username).then(() => { // Reset fields so the data is not in the view anymore. diff --git a/src/core/login/pages/site/site.html b/src/core/login/pages/site/site.html index 6a4b40f36..8da75c237 100644 --- a/src/core/login/pages/site/site.html +++ b/src/core/login/pages/site/site.html @@ -17,7 +17,7 @@
- +

{{ 'core.login.newsitedescription' | translate }}

diff --git a/src/core/login/pages/site/site.ts b/src/core/login/pages/site/site.ts index 5a231d27c..d20b510f7 100644 --- a/src/core/login/pages/site/site.ts +++ b/src/core/login/pages/site/site.ts @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component } from '@angular/core'; +import { Component, ViewChild, ElementRef } from '@angular/core'; import { IonicPage, NavController, ModalController, NavParams } from 'ionic-angular'; import { CoreAppProvider } from '@providers/app'; +import { CoreEventsProvider } from '@providers/events'; import { CoreSitesProvider, CoreSiteCheckResponse } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreConfigConstants } from '../../../../configconstants'; @@ -31,6 +32,9 @@ import { CoreUrl } from '@classes/utils/url'; templateUrl: 'site.html', }) export class CoreLoginSitePage { + + @ViewChild('siteFormEl') formElement: ElementRef; + siteForm: FormGroup; fixedSites: any[]; filteredSites: any[]; @@ -38,9 +42,15 @@ export class CoreLoginSitePage { showKeyboard = false; filter = ''; - constructor(navParams: NavParams, private navCtrl: NavController, fb: FormBuilder, private appProvider: CoreAppProvider, - private sitesProvider: CoreSitesProvider, private loginHelper: CoreLoginHelperProvider, - private modalCtrl: ModalController, private domUtils: CoreDomUtilsProvider) { + constructor(navParams: NavParams, + protected navCtrl: NavController, + fb: FormBuilder, + protected appProvider: CoreAppProvider, + protected sitesProvider: CoreSitesProvider, + protected loginHelper: CoreLoginHelperProvider, + protected modalCtrl: ModalController, + protected domUtils: CoreDomUtilsProvider, + protected eventsProvider: CoreEventsProvider) { this.showKeyboard = !!navParams.get('showKeyboard'); @@ -96,6 +106,12 @@ export class CoreLoginSitePage { // It's a demo site. this.sitesProvider.getUserToken(siteData.url, siteData.username, siteData.password).then((data) => { return this.sitesProvider.newSite(data.siteUrl, data.token, data.privateToken).then(() => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }); + return this.loginHelper.goToSiteInitialPage(); }, (error) => { this.loginHelper.treatUserTokenError(siteData.url, error, siteData.username, siteData.password); @@ -175,6 +191,12 @@ export class CoreLoginSitePage { */ protected async login(response: CoreSiteCheckResponse): Promise { return this.sitesProvider.checkRequiredMinimumVersion(response.config).then(() => { + + this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, { + form: this.formElement.nativeElement, + online: true, + }); + if (response.warning) { this.domUtils.showErrorModal(response.warning, true, 4000); } diff --git a/src/core/search/components/search-box/core-search-box.html b/src/core/search/components/search-box/core-search-box.html index adc00c50b..be186a40b 100644 --- a/src/core/search/components/search-box/core-search-box.html +++ b/src/core/search/components/search-box/core-search-box.html @@ -1,5 +1,5 @@ - +