MOBILE-3323 editor: Delete draft when form is submitted
parent
5a79151b01
commit
7fa8e6fe05
|
@ -9,7 +9,7 @@
|
|||
</ion-refresher>
|
||||
|
||||
<core-loading [hideUntil]="loaded">
|
||||
<form ion-list [formGroup]="eventForm" *ngIf="!error">
|
||||
<form ion-list [formGroup]="eventForm" *ngIf="!error" #editEventForm>
|
||||
<!-- Event name. -->
|
||||
<ion-item text-wrap>
|
||||
<ion-label stacked><h2 [core-mark-required]="true">{{ 'addon.calendar.eventname' | translate }}</h2></ion-label>
|
||||
|
|
|
@ -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 :
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<form name="addon-mod_assign-edit-feedback-form" *ngIf="userId && plugin">
|
||||
<form name="addon-mod_assign-edit-feedback-form" *ngIf="userId && plugin" #editFeedbackForm>
|
||||
<addon-mod-assign-feedback-plugin [assign]="assign" [submission]="submission" [userId]="userId" [plugin]="plugin" [edit]="true"></addon-mod-assign-feedback-plugin>
|
||||
<button ion-button block (click)="done($event)">{{ 'core.done' | translate }}</button>
|
||||
</form>
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<core-loading [hideUntil]="loaded">
|
||||
<ion-list>
|
||||
<!-- @todo: plagiarism_print_disclosure -->
|
||||
<form name="addon-mod_assign-edit-form" *ngIf="userSubmission && userSubmission.plugins && userSubmission.plugins.length">
|
||||
<form name="addon-mod_assign-edit-form" *ngIf="userSubmission && userSubmission.plugins && userSubmission.plugins.length" #editSubmissionForm>
|
||||
<!-- Submission statement. -->
|
||||
<ion-item text-wrap *ngIf="submissionStatement">
|
||||
<ion-label><core-format-text [text]="submissionStatement" [filter]="false"></core-format-text></ion-label>
|
||||
|
|
|
@ -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,50 +268,60 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
|||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected saveSubmission(): Promise<any> {
|
||||
protected async saveSubmission(): Promise<void> {
|
||||
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) => {
|
||||
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) => {
|
||||
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,
|
||||
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.
|
||||
promise = this.assignProvider.saveSubmission(this.assign.id, this.courseId, pluginData, this.allowOffline,
|
||||
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.
|
||||
await this.assignHelper.clearSubmissionPluginTmpData(this.assign, this.userSubmission, inputData);
|
||||
|
||||
// Submission saved, trigger events.
|
||||
this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, {
|
||||
form: this.formElement.nativeElement,
|
||||
online: sent,
|
||||
}, this.sitesProvider.getCurrentSiteId());
|
||||
|
||||
const params = {
|
||||
assignmentId: this.assign.id,
|
||||
submissionId: this.userSubmission.id,
|
||||
|
@ -323,11 +336,9 @@ export class AddonModAssignEditPage implements OnInit, OnDestroy {
|
|||
this.eventsProvider.trigger(AddonModAssignProvider.SUBMITTED_FOR_GRADING_EVENT, params,
|
||||
this.sitesProvider.getCurrentSiteId());
|
||||
}
|
||||
});
|
||||
});
|
||||
}).finally(() => {
|
||||
} finally {
|
||||
modal.dismiss();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<div class="addon-data-contents addon-data-entries-{{data.id}}" *ngIf="data">
|
||||
<core-style [css]="data.csstemplate" prefix=".addon-data-entries-{{data.id}}"></core-style>
|
||||
|
||||
<form (ngSubmit)="save($event)" [formGroup]="editForm">
|
||||
<form (ngSubmit)="save($event)" [formGroup]="editForm" #editFormEl>
|
||||
<core-compile-html [text]="editFormRender" [jsData]="jsData" [extraImports]="extraImports"></core-compile-html>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<a class="tab-slide" [attr.aria-selected]="!search.searchingAdvanced" (click)="changeAdvanced(false)">{{ 'addon.mod_data.search' | translate}}</a>
|
||||
<a class="tab-slide" [attr.aria-selected]="search.searchingAdvanced" (click)="changeAdvanced(true)">{{ 'addon.mod_data.advancedsearch' | translate }}</a>
|
||||
</div>
|
||||
<form (ngSubmit)="searchEntries($event)" [formGroup]="searchForm">
|
||||
<form (ngSubmit)="searchEntries($event)" [formGroup]="searchForm" #searchFormEl>
|
||||
<ion-list no-margin>
|
||||
<ion-item [hidden]="search.searchingAdvanced">
|
||||
<ion-input type="text" placeholder="{{ 'addon.mod_data.search' | translate}}" [(ngModel)]="search.text" name="text" formControlName="text"></ion-input>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<any> => {
|
||||
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<any> => {
|
||||
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);
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
<!-- Input password for protected lessons. -->
|
||||
<ion-card *ngIf="askPassword">
|
||||
<form ion-list (ngSubmit)="submitPassword($event, passwordinput)">
|
||||
<form ion-list (ngSubmit)="submitPassword($event, passwordinput)" #passwordForm>
|
||||
<ion-item text-wrap>
|
||||
<ion-label stacked>{{ 'addon.mod_lesson.enterpassword' | translate }}</ion-label>
|
||||
<core-show-password item-content [name]="'password'">
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content padding class="addon-mod_lesson-password-modal">
|
||||
<form ion-list (ngSubmit)="submitPassword($event, passwordinput)">
|
||||
<form ion-list (ngSubmit)="submitPassword($event, passwordinput)" #passwordForm>
|
||||
<ion-item>
|
||||
<core-show-password item-content [name]="'password'">
|
||||
<ion-label>{{ 'addon.mod_lesson.enterpassword' | translate }}</ion-label>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
<!-- Question page. -->
|
||||
<!-- We need to set ngIf loaded to make formGroup directive restart every time a page changes, see MOBILE-2540. -->
|
||||
<form *ngIf="question && loaded" ion-list [formGroup]="questionForm">
|
||||
<form *ngIf="question && loaded" ion-list [formGroup]="questionForm" #questionFormEl>
|
||||
<ion-item-divider text-wrap *ngIf="pageContent">
|
||||
<core-format-text [component]="component" [componentId]="lesson.coursemodule" [text]="pageContent" contextLevel="module" [contextInstanceId]="lesson.coursemodule" [courseId]="courseId"></core-format-text>
|
||||
</ion-item-divider>
|
||||
|
|
|
@ -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<any> {
|
||||
protected processPage(data: any, formSubmitted?: boolean): Promise<any> {
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
</div>
|
||||
|
||||
<!-- Questions -->
|
||||
<form name="addon-mod_quiz-player-form" *ngIf="questions && questions.length && !quizAborted && !showSummary">
|
||||
<form name="addon-mod_quiz-player-form" *ngIf="questions && questions.length && !quizAborted && !showSummary" #quizForm>
|
||||
<div *ngFor="let question of questions">
|
||||
<ion-card id="addon-mod_quiz-question-{{question.slot}}">
|
||||
<!-- "Header" of the question. -->
|
||||
|
|
|
@ -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<CoreQuestionComponent>;
|
||||
@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());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
</ion-header>
|
||||
<ion-content padding class="addon-mod_quiz-preflight-modal">
|
||||
<core-loading [hideUntil]="loaded">
|
||||
<form ion-list [formGroup]="preflightForm" (ngSubmit)="sendData($event)">
|
||||
<form ion-list [formGroup]="preflightForm" (ngSubmit)="sendData($event)" #preflightFormEl>
|
||||
<!-- Access rules. -->
|
||||
<ng-container *ngFor="let data of accessRulesData; let last = last">
|
||||
<core-dynamic-component [component]="data.component" [data]="data.data">
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</ion-header>
|
||||
<ion-content>
|
||||
<core-loading [hideUntil]="loaded">
|
||||
<form ion-list [formGroup]="pageForm">
|
||||
<form ion-list [formGroup]="pageForm" #editPageForm>
|
||||
<ion-item text-wrap *ngIf="canEditTitle" class="item-title">
|
||||
<ion-input name="title" type="text" [placeholder]="'addon.mod_wiki.newpagetitle' | translate" [formControlName]="'title'"></ion-input>
|
||||
</ion-item>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<h3 padding>{{ 'addon.mod_workshop.assessmentform' | translate }}</h3>
|
||||
|
||||
<form name="mma-mod_workshop-assessment-form">
|
||||
<form name="mma-mod_workshop-assessment-form" #assessmentForm>
|
||||
<core-loading [hideUntil]="assessmentStrategyLoaded">
|
||||
<ng-container *ngIf="componentClass && assessmentStrategyLoaded">
|
||||
<core-dynamic-component [component]="componentClass" [data]="data"></core-dynamic-component>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
<addon-mod-workshop-assessment-strategy *ngIf="assessment && assessmentId && showGrade(assessment.grade) && workshop && access" [workshop]="workshop" [access]="access" [assessmentId]="assessmentId" [userId]="profile && profile.id" [strategy]="strategy"></addon-mod-workshop-assessment-strategy>
|
||||
|
||||
<form ion-list [formGroup]="evaluateForm" *ngIf="evaluating">
|
||||
<form ion-list [formGroup]="evaluateForm" *ngIf="evaluating" #evaluateFormEl>
|
||||
<ion-item text-wrap>
|
||||
<h2>{{ 'addon.mod_workshop.assessmentsettings' | translate }}</h2>
|
||||
</ion-item>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
</ion-header>
|
||||
<ion-content>
|
||||
<core-loading [hideUntil]="loaded">
|
||||
<form ion-list [formGroup]="editForm" *ngIf="workshop">
|
||||
<form ion-list [formGroup]="editForm" *ngIf="workshop" #editFormEl>
|
||||
<ion-item text-wrap>
|
||||
<ion-label stacked core-mark-required="true">{{ 'addon.mod_workshop.submissiontitle' | translate }}</ion-label>
|
||||
<ion-input name="title" type="text" [placeholder]="'addon.mod_workshop.submissiontitle' | translate" formControlName="title"></ion-input>
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -366,7 +368,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, 'add').then(() => {
|
||||
// Don't return anything.
|
||||
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
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
<addon-mod-workshop-assessment *ngFor="let reviewer of submissionInfo.reviewerof" [assessment]="reviewer" [courseId]="courseId" summary="true" [workshop]="workshop" [access]="access"></addon-mod-workshop-assessment>
|
||||
</ion-list>
|
||||
|
||||
<form ion-list [formGroup]="feedbackForm" *ngIf="canAddFeedback">
|
||||
<form ion-list [formGroup]="feedbackForm" *ngIf="canAddFeedback" #feedbackFormEl>
|
||||
<ion-item text-wrap>
|
||||
<h2>{{ 'addon.mod_workshop.feedbackauthor' | translate }}</h2>
|
||||
</ion-item>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<form name="itemEdit" (ngSubmit)="addNote($event)">
|
||||
<form name="itemEdit" (ngSubmit)="addNote($event)" #itemEdit>
|
||||
<ion-item>
|
||||
<ion-label>{{ 'addon.notes.publishstate' | translate }}</ion-label>
|
||||
<ion-select [(ngModel)]="type" name="publishState" interface="popover">
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<form (ngSubmit)="changeName(newFileName, $event)">
|
||||
<form (ngSubmit)="changeName(newFileName, $event)" #nameForm>
|
||||
<a ion-item text-wrap stacked class="item-media" [class.item-input]="editMode" (click)="fileClicked($event)" detail-none>
|
||||
<img [src]="fileIcon" alt="{{fileExtension}}" role="presentation" item-start />
|
||||
|
||||
|
|
|
@ -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<any>; // Will notify when the file is renamed. Receives the FileEntry as the param.
|
||||
@Output() onClick?: EventEmitter<void>; // 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();
|
||||
|
|
|
@ -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:
|
||||
* <core-search-box (onSubmit)="search($event)" [placeholder]="'core.courses.search' | translate"
|
||||
* [searchLabel]="'core.courses.search' | translate" autoFocus="true"></core-search-box>
|
||||
*/
|
||||
@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<string>; // Send data when submitting the search form.
|
||||
@Output() onClear: EventEmitter<void>; // 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<string>();
|
||||
this.onClear = new EventEmitter<void>();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
<form>
|
||||
<form #messageForm>
|
||||
<textarea class="core-send-message-input" [core-auto-focus]="showKeyboard" [placeholder]="placeholder" rows="1" core-auto-rows [(ngModel)]="message" name="message" (onResize)="textareaResized()" (keydown.enter)="enterClicked($event)" (keydown.control.enter)="enterClicked($event, 'control')" (keydown.meta.enter)="enterClicked($event, 'meta')" aria-multiline="true"></textarea>
|
||||
<ion-buttons end>
|
||||
<button ion-button icon-only clear="true" type="submit" [disabled]="!message || sendDisabled" [attr.aria-label]="'core.send' | translate" [core-suppress-events] (onClick)="submitForm($event)">
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
||||
import { Component, Input, Output, EventEmitter, OnInit, ViewChild, ElementRef } from '@angular/core';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreConfigProvider } from '@providers/config';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
|
@ -43,10 +43,16 @@ export class CoreSendMessageFormComponent implements OnInit {
|
|||
@Output() onSubmit: EventEmitter<string>; // Send data when submitting the message form.
|
||||
@Output() onResize: EventEmitter<void>; // Emit when resizing the textarea.
|
||||
|
||||
@ViewChild('messageForm') formElement: ElementRef;
|
||||
|
||||
protected sendOnEnter: boolean;
|
||||
|
||||
constructor(private utils: CoreUtilsProvider, private textUtils: CoreTextUtilsProvider, configProvider: CoreConfigProvider,
|
||||
eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider) {
|
||||
constructor(protected utils: CoreUtilsProvider,
|
||||
protected textUtils: CoreTextUtilsProvider,
|
||||
configProvider: CoreConfigProvider,
|
||||
protected eventsProvider: CoreEventsProvider,
|
||||
protected sitesProvider: CoreSitesProvider,
|
||||
protected appProvider: CoreAppProvider) {
|
||||
|
||||
this.onSubmit = new EventEmitter();
|
||||
this.onResize = new EventEmitter();
|
||||
|
@ -82,6 +88,11 @@ export class CoreSendMessageFormComponent implements OnInit {
|
|||
|
||||
this.message = ''; // Reset the form.
|
||||
|
||||
this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, {
|
||||
form: this.formElement.nativeElement,
|
||||
online: false,
|
||||
}, this.sitesProvider.getCurrentSiteId());
|
||||
|
||||
value = this.textUtils.replaceNewLines(value, '<br>');
|
||||
this.onSubmit.emit(value);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<form name="itemEdit" (ngSubmit)="addComment($event)">
|
||||
<form name="itemEdit" (ngSubmit)="addComment($event)" #commentForm>
|
||||
<ion-item>
|
||||
<ion-textarea placeholder="{{ 'core.comments.addcomment' | translate }}" rows="5" [(ngModel)]="content" name="content" required="required"></ion-textarea>
|
||||
</ion-item>
|
||||
|
|
|
@ -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 { CoreCommentsProvider } from '../../providers/comments';
|
||||
|
||||
|
@ -27,6 +29,8 @@ import { CoreCommentsProvider } from '../../providers/comments';
|
|||
templateUrl: 'add.html',
|
||||
})
|
||||
export class CoreCommentsAddPage {
|
||||
@ViewChild('commentForm') formElement: ElementRef;
|
||||
|
||||
protected contextLevel: string;
|
||||
protected instanceId: number;
|
||||
protected componentName: string;
|
||||
|
@ -36,8 +40,13 @@ export class CoreCommentsAddPage {
|
|||
content = '';
|
||||
processing = false;
|
||||
|
||||
constructor(params: NavParams, private viewCtrl: ViewController, private appProvider: CoreAppProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private commentsProvider: CoreCommentsProvider) {
|
||||
constructor(params: NavParams,
|
||||
protected viewCtrl: ViewController,
|
||||
protected appProvider: CoreAppProvider,
|
||||
protected domUtils: CoreDomUtilsProvider,
|
||||
protected commentsProvider: CoreCommentsProvider,
|
||||
protected eventsProvider: CoreEventsProvider,
|
||||
protected sitesProvider: CoreSitesProvider) {
|
||||
this.contextLevel = params.get('contextLevel');
|
||||
this.instanceId = params.get('instanceId');
|
||||
this.componentName = params.get('componentName');
|
||||
|
@ -61,6 +70,12 @@ export class CoreCommentsAddPage {
|
|||
this.processing = true;
|
||||
this.commentsProvider.addComment(this.content, this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
||||
this.area).then((commentsResponse) => {
|
||||
|
||||
this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, {
|
||||
form: this.formElement.nativeElement,
|
||||
online: !!commentsResponse,
|
||||
}, this.sitesProvider.getCurrentSiteId());
|
||||
|
||||
this.viewCtrl.dismiss({comments: commentsResponse}).finally(() => {
|
||||
this.domUtils.showToast(commentsResponse ? 'core.comments.eventcommentcreated' : 'core.datastoredoffline', true,
|
||||
3000);
|
||||
|
|
|
@ -54,7 +54,7 @@ export class CoreCommentsProvider {
|
|||
// Convenience function to store a comment to be synchronized later.
|
||||
const storeOffline = (): Promise<any> => {
|
||||
return this.commentsOffline.saveComment(content, contextLevel, instanceId, component, itemId, area, siteId).then(() => {
|
||||
return Promise.resolve(false);
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<form ion-list #f="ngForm" (ngSubmit)="submitPassword($event, f.value.password)">
|
||||
<form ion-list #f="ngForm" (ngSubmit)="submitPassword($event, f.value.password)" #enrolPasswordForm>
|
||||
<ion-item>
|
||||
<core-show-password item-content [name]="'password'">
|
||||
<ion-input text-wrap class="core-ioninput-password" name="password" type="password" placeholder="{{ 'core.courses.password' | translate }}" ngModel [core-auto-focus] [clearOnEdit]="false"></ion-input>
|
||||
|
|
|
@ -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';
|
||||
|
||||
/**
|
||||
* Page that displays a form to enter a password to self enrol in a course.
|
||||
|
@ -24,7 +26,12 @@ import { IonicPage, ViewController } from 'ionic-angular';
|
|||
templateUrl: 'self-enrol-password.html',
|
||||
})
|
||||
export class CoreCoursesSelfEnrolPasswordPage {
|
||||
constructor(private viewCtrl: ViewController) { }
|
||||
|
||||
@ViewChild('enrolPasswordForm') formElement: ElementRef;
|
||||
|
||||
constructor(protected viewCtrl: ViewController,
|
||||
protected eventsProvider: CoreEventsProvider,
|
||||
protected sitesProvider: CoreSitesProvider) { }
|
||||
|
||||
/**
|
||||
* Close help modal.
|
||||
|
@ -43,6 +50,11 @@ export class CoreCoursesSelfEnrolPasswordPage {
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.eventsProvider.trigger(CoreEventsProvider.FORM_SUBMITTED, {
|
||||
form: this.formElement.nativeElement,
|
||||
online: false,
|
||||
}, this.sitesProvider.getCurrentSiteId());
|
||||
|
||||
this.viewCtrl.dismiss(password);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,8 @@ export class CoreEditorRichTextEditorComponent implements AfterContentInit, OnDe
|
|||
|
||||
protected valueChangeSubscription: Subscription;
|
||||
protected keyboardObs: any;
|
||||
protected initHeightInterval;
|
||||
protected resetObs: any;
|
||||
protected initHeightInterval: NodeJS.Timer;
|
||||
|
||||
rteEnabled = false;
|
||||
editorSupported = true;
|
||||
|
@ -175,11 +176,11 @@ export class CoreEditorRichTextEditorComponent implements AfterContentInit, OnDe
|
|||
}
|
||||
|
||||
if (this.shouldAutoSaveDrafts()) {
|
||||
// Recover drafts.
|
||||
this.restoreDraft();
|
||||
|
||||
// Auto save drafts every certain time.
|
||||
this.autoSaveDrafts();
|
||||
|
||||
this.deleteDraftOnSubmit();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -780,6 +781,25 @@ export class CoreEditorRichTextEditorComponent implements AfterContentInit, OnDe
|
|||
}, this.DRAFT_AUTOSAVE_FREQUENCY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the draft when the form is submitted.
|
||||
*/
|
||||
protected deleteDraftOnSubmit(): void {
|
||||
|
||||
this.resetObs = this.events.on(CoreEventsProvider.FORM_SUBMITTED, async (data) => {
|
||||
const form = this.element.closest('form');
|
||||
|
||||
if (data.form && form && data.form == form) {
|
||||
try {
|
||||
await this.editorOffline.deleteDraft(this.contextLevel, this.contextInstanceId, this.elementId,
|
||||
this.draftExtraParams);
|
||||
} catch (error) {
|
||||
// Error deleting draft. Shouldn't happen.
|
||||
}
|
||||
}
|
||||
}, this.sitesProvider.getCurrentSiteId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a message.
|
||||
*
|
||||
|
@ -824,5 +844,6 @@ export class CoreEditorRichTextEditorComponent implements AfterContentInit, OnDe
|
|||
this.keyboardObs && this.keyboardObs.off();
|
||||
clearInterval(this.autoSaveInterval);
|
||||
clearTimeout(this.hideMessageTimeout);
|
||||
this.resetObs && this.resetObs.off();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<p *ngIf="siteName" padding class="item-heading core-sitename"><core-format-text [text]="siteName" [filter]="false"></core-format-text></p>
|
||||
<p *ngIf="siteName" class="core-siteurl">{{siteUrl}}</p>
|
||||
</div>
|
||||
<form ion-list [formGroup]="credForm" (ngSubmit)="login($event)" class="core-login-form">
|
||||
<form ion-list [formGroup]="credForm" (ngSubmit)="login($event)" class="core-login-form" #credentialsForm>
|
||||
<ion-item *ngIf="siteChecked && !isBrowserSSO">
|
||||
<ion-input type="text" name="username" placeholder="{{ 'core.login.username' | translate }}" formControlName="username" autocapitalize="none" autocorrect="off"></ion-input>
|
||||
</ion-item>
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<core-loading [hideUntil]="settingsLoaded" *ngIf="!isMinor">
|
||||
|
||||
<!-- Age verification. -->
|
||||
<form ion-list *ngIf="settingsLoaded && settings && ageDigitalConsentVerification" [formGroup]="ageVerificationForm" (ngSubmit)="verifyAge($event)">
|
||||
<form ion-list *ngIf="settingsLoaded && settings && ageDigitalConsentVerification" [formGroup]="ageVerificationForm" (ngSubmit)="verifyAge($event)" #ageForm>
|
||||
<ion-item-divider text-wrap>
|
||||
<p class="item-heading">{{ 'core.agelocationverification' | translate }}</p>
|
||||
</ion-item-divider>
|
||||
|
@ -47,7 +47,7 @@
|
|||
</form>
|
||||
|
||||
<!-- Signup form. -->
|
||||
<form ion-list *ngIf="settingsLoaded && settings && !ageDigitalConsentVerification" [formGroup]="signupForm" (ngSubmit)="create($event)">
|
||||
<form ion-list *ngIf="settingsLoaded && settings && !ageDigitalConsentVerification" [formGroup]="signupForm" (ngSubmit)="create($event)" #signupFormEl>
|
||||
<ion-item text-wrap text-center>
|
||||
<!-- If no sitename show big siteurl. -->
|
||||
<p *ngIf="!siteName" padding class="item-heading">{{siteUrl}}</p>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
</ion-item>
|
||||
</ion-list>
|
||||
<ion-card>
|
||||
<form ion-list [formGroup]="myForm" (ngSubmit)="resetPassword($event)">
|
||||
<form ion-list [formGroup]="myForm" (ngSubmit)="resetPassword($event)" #resetPasswordForm>
|
||||
<ion-item-divider text-wrap>
|
||||
{{ 'core.login.searchby' | translate }}
|
||||
</ion-item-divider>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<ion-icon padding name="alert"></ion-icon> {{ 'core.login.reconnectdescription' | translate }}
|
||||
</p>
|
||||
</div>
|
||||
<form ion-list *ngIf="!isOAuth" [formGroup]="credForm" (ngSubmit)="login($event)" class="core-login-form">
|
||||
<form ion-list *ngIf="!isOAuth" [formGroup]="credForm" (ngSubmit)="login($event)" class="core-login-form" #reconnectForm>
|
||||
<ion-item text-wrap class="core-username">
|
||||
<p>{{username}}</p>
|
||||
</ion-item>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<div text-center padding>
|
||||
<img src="assets/img/login_logo.png" class="avatar-full login-logo" role="presentation">
|
||||
</div>
|
||||
<form ion-list [formGroup]="siteForm" (ngSubmit)="connect($event, siteForm.value.siteUrl)" *ngIf="!fixedSites || fixedDisplay == 'select'">
|
||||
<form ion-list [formGroup]="siteForm" (ngSubmit)="connect($event, siteForm.value.siteUrl)" *ngIf="!fixedSites || fixedDisplay == 'select'" #siteFormEl>
|
||||
<!-- Form to input the site URL if there are no fixed sites. -->
|
||||
<ng-container *ngIf="!fixedSites">
|
||||
<p padding>{{ 'core.login.newsitedescription' | translate }}</p>
|
||||
|
|
|
@ -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<void> {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<ion-card>
|
||||
<form #f="ngForm" (ngSubmit)="submitForm($event)" role="search">
|
||||
<form #f="ngForm" (ngSubmit)="submitForm($event)" role="search" #searchForm>
|
||||
<ion-item>
|
||||
<ion-input type="search" name="search" [(ngModel)]="searchText" [placeholder]="placeholder" [autocorrect]="autocorrect" [spellcheck]="spellcheck" [core-auto-focus]="autoFocus" [disabled]="disabled" role="searchbox" (focus)="showHistory()" (blur)="hideHistory()"></ion-input>
|
||||
<button item-end ion-button clear icon-only type="submit" class="button-small" [attr.aria-label]="searchLabel" [disabled]="disabled || !searchText || (searchText.length < lengthCheck)">
|
||||
|
|
|
@ -12,8 +12,11 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, Input, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreSearchHistoryProvider, CoreSearchHistoryItem } from '../../providers/search-history';
|
||||
|
||||
|
@ -47,6 +50,8 @@ export class CoreSearchBoxComponent implements OnInit, OnDestroy {
|
|||
@Output() onSubmit: EventEmitter<string>; // Send data when submitting the search form.
|
||||
@Output() onClear: EventEmitter<void>; // Send event when clearing the search form.
|
||||
|
||||
@ViewChild('searchForm') formElement: ElementRef;
|
||||
|
||||
searched = ''; // Last search emitted.
|
||||
searchText = '';
|
||||
history: CoreSearchHistoryItem[];
|
||||
|
@ -58,6 +63,9 @@ export class CoreSearchBoxComponent implements OnInit, OnDestroy {
|
|||
constructor(protected translate: TranslateService,
|
||||
protected utils: CoreUtilsProvider,
|
||||
protected searchHistoryProvider: CoreSearchHistoryProvider,
|
||||
protected eventsProvider: CoreEventsProvider,
|
||||
protected sitesProvider: CoreSitesProvider,
|
||||
protected domUtils: CoreDomUtilsProvider,
|
||||
) {
|
||||
this.onSubmit = new EventEmitter<string>();
|
||||
this.onClear = new EventEmitter<void>();
|
||||
|
@ -95,6 +103,8 @@ export class CoreSearchBoxComponent implements OnInit, OnDestroy {
|
|||
this.saveSearchToHistory(this.searchText);
|
||||
}
|
||||
|
||||
this.domUtils.triggerFormSubmittedEvent(this.formElement.nativeElement, false, this.sitesProvider.getCurrentSiteId());
|
||||
|
||||
this.searched = this.searchText;
|
||||
this.onSubmit.emit(this.searchText);
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ export class CoreEventsProvider {
|
|||
static SELECT_COURSE_TAB = 'select_course_tab';
|
||||
static WS_CACHE_INVALIDATED = 'ws_cache_invalidated';
|
||||
static SITE_STORAGE_DELETED = 'site_storage_deleted';
|
||||
static FORM_SUBMITTED = 'form_submitted';
|
||||
|
||||
protected logger;
|
||||
protected observables: { [s: string]: Subject<any> } = {};
|
||||
|
|
Loading…
Reference in New Issue