// (C) Copyright 2015 Martin Dougiamas // // 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, OnInit, Optional, ViewChild } from '@angular/core'; import { FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { CoreEventsProvider } from '@providers/events'; import { CoreGroupsProvider } from '@providers/groups'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCoursesProvider } from '@core/courses/providers/courses'; import { CoreSplitViewComponent } from '@components/split-view/split-view'; import { CoreRichTextEditorComponent } from '@components/rich-text-editor/rich-text-editor.ts'; import { AddonCalendarProvider } from '../../providers/calendar'; import { AddonCalendarHelperProvider } from '../../providers/helper'; import { CoreSite } from '@classes/site'; /** * Page that displays a form to create/edit an event. */ @IonicPage({ segment: 'addon-calendar-edit-event' }) @Component({ selector: 'page-addon-calendar-edit-event', templateUrl: 'edit-event.html', }) export class AddonCalendarEditEventPage implements OnInit { @ViewChild(CoreRichTextEditorComponent) descriptionEditor: CoreRichTextEditorComponent; title: string; dateFormat: string; component = AddonCalendarProvider.COMPONENT; loaded = false; hasOffline = false; eventTypes = []; categories = []; courses = []; groups = []; loadingGroups = false; courseGroupSet = false; advanced = false; errors: any; // Form variables. eventForm: FormGroup; eventTypeControl: FormControl; groupControl: FormControl; descriptionControl: FormControl; protected eventId: number; protected courseId: number; protected originalData: any; protected currentSite: CoreSite; protected types: any; // Object with the supported types. protected showAll: boolean; constructor(navParams: NavParams, private navCtrl: NavController, private translate: TranslateService, private domUtils: CoreDomUtilsProvider, private timeUtils: CoreTimeUtilsProvider, private eventsProvider: CoreEventsProvider, private groupsProvider: CoreGroupsProvider, sitesProvider: CoreSitesProvider, private coursesProvider: CoreCoursesProvider, private utils: CoreUtilsProvider, private calendarProvider: AddonCalendarProvider, private calendarHelper: AddonCalendarHelperProvider, private fb: FormBuilder, @Optional() private svComponent: CoreSplitViewComponent) { this.eventId = navParams.get('eventId'); this.courseId = navParams.get('courseId'); this.title = this.eventId ? 'addon.calendar.editevent' : 'addon.calendar.newevent'; this.currentSite = sitesProvider.getCurrentSite(); this.errors = { required: this.translate.instant('core.required') }; // Calculate format to use. ion-datetime doesn't support escaping characters ([]), so we remove them. this.dateFormat = this.timeUtils.convertPHPToMoment(this.translate.instant('core.strftimedatetimeshort')) .replace(/[\[\]]/g, ''); // Initialize form variables. this.eventForm = new FormGroup({}); this.eventTypeControl = this.fb.control('', Validators.required); this.groupControl = this.fb.control(''); this.descriptionControl = this.fb.control(''); this.eventForm.addControl('name', this.fb.control('', Validators.required)); this.eventForm.addControl('timestart', this.fb.control(new Date().toISOString(), Validators.required)); this.eventForm.addControl('eventtype', this.eventTypeControl); this.eventForm.addControl('categoryid', this.fb.control('')); this.eventForm.addControl('courseid', this.fb.control(this.courseId)); this.eventForm.addControl('groupcourseid', this.fb.control('')); this.eventForm.addControl('groupid', this.groupControl); this.eventForm.addControl('description', this.descriptionControl); this.eventForm.addControl('location', this.fb.control('')); this.eventForm.addControl('duration', this.fb.control(0)); this.eventForm.addControl('timedurationuntil', this.fb.control(new Date().toISOString())); this.eventForm.addControl('timedurationminutes', this.fb.control('')); this.eventForm.addControl('repeat', this.fb.control(false)); this.eventForm.addControl('repeats', this.fb.control('1')); } /** * Component being initialized. */ ngOnInit(): void { this.fetchData().finally(() => { this.originalData = this.utils.clone(this.eventForm.value); this.loaded = true; }); } /** * Fetch the data needed to render the form. * * @return {Promise} Promise resolved when done. */ protected fetchData(): Promise { let accessInfo; // Get access info. return this.calendarProvider.getAccessInformation().then((info) => { accessInfo = info; return this.calendarProvider.getAllowedEventTypes(); }).then((types) => { this.types = types; const promises = [], eventTypes = this.calendarHelper.getEventTypeOptions(types); if (!eventTypes.length) { return Promise.reject(this.translate.instant('addon.calendar.nopermissiontoupdatecalendar')); } if (types.category) { // Get the categories. promises.push(this.coursesProvider.getCategories(0, true).then((cats) => { this.categories = cats; })); } this.showAll = this.utils.isTrueOrOne(this.currentSite.getStoredConfig('calendar_adminseesall')) && accessInfo.canmanageentries; if (types.course || types.groups) { // Get the courses. const promise = this.showAll ? this.coursesProvider.getCoursesByField() : this.coursesProvider.getUserCourses(); promises.push(promise.then((courses) => { if (this.showAll) { // Remove site home from the list of courses. const siteHomeId = this.currentSite.getSiteHomeId(); courses = courses.filter((course) => { return course.id != siteHomeId; }); } // Sort courses by name. this.courses = courses.sort((a, b) => { const compareA = a.fullname.toLowerCase(), compareB = b.fullname.toLowerCase(); return compareA.localeCompare(compareB); }); })); } return Promise.all(promises).then(() => { // Set event types. If course is allowed, select it first. if (types.course) { this.eventTypeControl.setValue(AddonCalendarProvider.TYPE_COURSE); } else { this.eventTypeControl.setValue(eventTypes[0].value); } this.eventTypes = eventTypes; }); }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'Error getting data.'); this.originalData = null; // Avoid asking for confirmation. this.navCtrl.pop(); }); } /** * Pull to refresh. * * @param {any} refresher Refresher. */ refreshData(refresher: any): void { const promises = [ this.calendarProvider.invalidateAccessInformation(this.courseId), this.calendarProvider.invalidateAllowedEventTypes(this.courseId) ]; if (this.types) { if (this.types.category) { promises.push(this.coursesProvider.invalidateCategories(0, true)); } if (this.types.course || this.types.groups) { if (this.showAll) { promises.push(this.coursesProvider.invalidateCoursesByField()); } else { promises.push(this.coursesProvider.invalidateUserCourses()); } } } Promise.all(promises).finally(() => { this.fetchData().finally(() => { refresher.complete(); }); }); } /** * A course was selected, get its groups. * * @param {number} courseId Course ID. */ groupCourseSelected(courseId: number): void { if (!courseId) { return; } const modal = this.domUtils.showModalLoading(); this.loadingGroups = true; this.groupsProvider.getUserGroupsInCourse(courseId).then((groups) => { this.groups = groups; this.courseGroupSet = true; this.groupControl.setValue(''); }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'Error getting data.'); }).finally(() => { this.loadingGroups = false; modal.dismiss(); }); } /** * Show or hide advanced form fields. */ toggleAdvanced(): void { this.advanced = !this.advanced; } /** * Create the event. */ submit(): void { // Validate data. const formData = this.eventForm.value, timeStartDate = new Date(formData.timestart), timeUntilDate = new Date(formData.timedurationuntil), timeDurationMinutes = parseInt(formData.timedurationminutes || '', 10); let error; if (formData.eventtype == AddonCalendarProvider.TYPE_COURSE && !formData.courseid) { error = 'core.selectacourse'; } else if (formData.eventtype == AddonCalendarProvider.TYPE_GROUP && !formData.groupcourseid) { error = 'core.selectacourse'; } else if (formData.eventtype == AddonCalendarProvider.TYPE_GROUP && !formData.groupid) { error = 'core.selectagroup'; } else if (formData.eventtype == AddonCalendarProvider.TYPE_CATEGORY && !formData.categoryid) { error = 'core.selectacategory'; } else if (formData.duration == 1 && timeStartDate.getTime() > timeUntilDate.getTime()) { error = 'addon.calendar.invalidtimedurationuntil'; } else if (formData.duration == 2 && (isNaN(timeDurationMinutes) || timeDurationMinutes < 1)) { error = 'addon.calendar.invalidtimedurationminutes'; } if (error) { // Show error and stop. this.domUtils.showErrorModal(this.translate.instant(error)); return; } // Format the data to send. const data: any = { name: formData.name, eventtype: formData.eventtype, timestart: Math.floor(timeStartDate.getTime() / 1000), description: { text: formData.description, format: 1 }, location: formData.location, duration: formData.duration, repeat: formData.repeat }; if (formData.eventtype == AddonCalendarProvider.TYPE_COURSE) { data.courseid = formData.courseid; } else if (formData.eventtype == AddonCalendarProvider.TYPE_GROUP) { data.groupcourseid = formData.groupcourseid; data.groupid = formData.groupid; } else if (formData.eventtype == AddonCalendarProvider.TYPE_CATEGORY) { data.categoryid = formData.categoryid; } if (formData.duration == 1) { data.timedurationuntil = Math.floor(timeUntilDate.getTime() / 1000); } else if (formData.duration == 2) { data.timedurationminutes = formData.timedurationminutes; } if (formData.repeat) { data.repeats = formData.repeats; } // Send the data. const modal = this.domUtils.showModalLoading('core.sending'); this.calendarProvider.submitEvent(this.eventId, data).then((event) => { this.returnToList(event); }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'Error sending data.'); }).finally(() => { modal.dismiss(); }); } /** * Convenience function to update or return to event list depending on device. * * @param {number} [event] Event. */ protected returnToList(event?: any): void { const data: any = { event: event }; this.eventsProvider.trigger(AddonCalendarProvider.NEW_EVENT_EVENT, data, this.currentSite.getId()); if (this.svComponent && this.svComponent.isOn()) { // Empty form. this.hasOffline = false; this.eventForm.reset(this.originalData); this.originalData = this.utils.clone(this.eventForm.value); } else { this.originalData = null; // Avoid asking for confirmation. this.navCtrl.pop(); } } /** * Discard an offline saved discussion. */ discard(): void { this.domUtils.showConfirm(this.translate.instant('core.areyousure')).then(() => { // @todo. }).catch(() => { // Cancelled. }); } /** * Check if we can leave the page or not. * * @return {boolean|Promise} Resolved if we can leave it, rejected if not. */ ionViewCanLeave(): boolean | Promise { if (this.calendarHelper.hasEventDataChanged(this.eventForm.value, this.originalData)) { // Show confirmation if some data has been modified. return this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit')); } else { return Promise.resolve(); } } }