From e740fe4205e1767da235f4d4f912ae200d9dc568 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 26 Jun 2019 08:16:12 +0200 Subject: [PATCH] MOBILE-3087 calendar: Support editing calendar events --- scripts/langindex.json | 1 + .../calendar/pages/edit-event/edit-event.html | 2 +- .../calendar/pages/edit-event/edit-event.ts | 61 ++++-- src/addon/calendar/pages/event/event.html | 16 +- src/addon/calendar/pages/event/event.ts | 197 ++++++++++++++++-- src/addon/calendar/pages/list/list.html | 2 +- src/addon/calendar/pages/list/list.ts | 42 +++- src/addon/calendar/providers/calendar-sync.ts | 12 +- src/addon/calendar/providers/calendar.ts | 17 +- src/assets/lang/en.json | 1 + src/lang/en.json | 1 + src/providers/utils/utils.ts | 4 +- 12 files changed, 292 insertions(+), 64 deletions(-) diff --git a/scripts/langindex.json b/scripts/langindex.json index dc86f5c89..6103e27f7 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -1484,6 +1484,7 @@ "core.image": "local_moodlemobileapp", "core.imageviewer": "local_moodlemobileapp", "core.info": "moodle", + "core.invalidformdata": "error", "core.ios": "local_moodlemobileapp", "core.labelsep": "langconfig", "core.lastaccess": "moodle", diff --git a/src/addon/calendar/pages/edit-event/edit-event.html b/src/addon/calendar/pages/edit-event/edit-event.html index 0cf401536..96edde1b9 100644 --- a/src/addon/calendar/pages/edit-event/edit-event.html +++ b/src/addon/calendar/pages/edit-event/edit-event.html @@ -134,7 +134,7 @@ - + diff --git a/src/addon/calendar/pages/edit-event/edit-event.ts b/src/addon/calendar/pages/edit-event/edit-event.ts index bf394c205..e800800c2 100644 --- a/src/addon/calendar/pages/edit-event/edit-event.ts +++ b/src/addon/calendar/pages/edit-event/edit-event.ts @@ -162,7 +162,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy { } if (this.eventId && !refresh) { - // If editing an event, get offline data. Wait for sync first. + // Editing an event, get the event data. Wait for sync first. promises.push(this.calendarSync.waitForSync(AddonCalendarSyncProvider.SYNC_ID).then(() => { // Do not block if the scope is already destroyed. @@ -170,29 +170,38 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy { this.syncProvider.blockOperation(AddonCalendarProvider.COMPONENT, this.eventId); } - // Get the event data if there's any. + // Get the event offline data if there's any. return this.calendarOffline.getEvent(this.eventId).then((event) => { this.hasOffline = true; - // Load the data in the form. - this.eventForm.controls.name.setValue(event.name); - this.eventForm.controls.timestart.setValue(this.timeUtils.toDatetimeFormat(event.timestart * 1000)); - this.eventForm.controls.eventtype.setValue(event.eventtype); - this.eventForm.controls.categoryid.setValue(event.categoryid || ''); - this.eventForm.controls.courseid.setValue(event.courseid || ''); - this.eventForm.controls.groupcourseid.setValue(event.groupcourseid || ''); - this.eventForm.controls.groupid.setValue(event.groupid || ''); - this.eventForm.controls.description.setValue(event.description); - this.eventForm.controls.location.setValue(event.location); - this.eventForm.controls.duration.setValue(event.duration); - this.eventForm.controls.timedurationuntil.setValue( - this.timeUtils.toDatetimeFormat((event.timedurationuntil * 1000) || Date.now())); - this.eventForm.controls.timedurationminutes.setValue(event.timedurationminutes || ''); - this.eventForm.controls.repeat.setValue(!!event.repeat); - this.eventForm.controls.repeats.setValue(event.repeats || '1'); + return event; }).catch(() => { // No offline data. this.hasOffline = false; + + if (this.eventId > 0) { + // It's an online event. get its data from server. + return this.calendarProvider.getEventById(this.eventId); + } + }).then((event) => { + if (event) { + // Load the data in the form. + this.eventForm.controls.name.setValue(event.name); + this.eventForm.controls.timestart.setValue(this.timeUtils.toDatetimeFormat(event.timestart * 1000)); + this.eventForm.controls.eventtype.setValue(event.eventtype); + this.eventForm.controls.categoryid.setValue(event.categoryid || ''); + this.eventForm.controls.courseid.setValue(event.courseid || ''); + this.eventForm.controls.groupcourseid.setValue(event.groupcourseid || ''); + this.eventForm.controls.groupid.setValue(event.groupid || ''); + this.eventForm.controls.description.setValue(event.description); + this.eventForm.controls.location.setValue(event.location); + this.eventForm.controls.duration.setValue(event.duration); + this.eventForm.controls.timedurationuntil.setValue( + this.timeUtils.toDatetimeFormat((event.timedurationuntil * 1000) || Date.now())); + this.eventForm.controls.timedurationminutes.setValue(event.timedurationminutes || ''); + this.eventForm.controls.repeat.setValue(!!event.repeat); + this.eventForm.controls.repeats.setValue(event.repeats || '1'); + } }); })); } @@ -379,7 +388,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy { } // Send the data. - const modal = this.domUtils.showModalLoading('core.sending'); + const modal = this.domUtils.showModalLoading('core.sending', true); this.calendarProvider.submitEvent(this.eventId, data).then((result) => { this.returnToList(result.event); @@ -399,13 +408,21 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy { // Unblock the sync because the view will be destroyed and the sync process could be triggered before ngOnDestroy. this.unblockSync(); - if (event) { + if (this.eventId > 0) { + // Editing an event. const data: any = { event: event }; - this.eventsProvider.trigger(AddonCalendarProvider.NEW_EVENT_EVENT, data, this.currentSite.getId()); + this.eventsProvider.trigger(AddonCalendarProvider.EDIT_EVENT_EVENT, data, this.currentSite.getId()); } else { - this.eventsProvider.trigger(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, {}, this.currentSite.getId()); + if (event) { + const data: any = { + event: event + }; + this.eventsProvider.trigger(AddonCalendarProvider.NEW_EVENT_EVENT, data, this.currentSite.getId()); + } else { + this.eventsProvider.trigger(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, {}, this.currentSite.getId()); + } } if (this.svComponent && this.svComponent.isOn()) { diff --git a/src/addon/calendar/pages/event/event.html b/src/addon/calendar/pages/event/event.html index d823697c6..93e5bd285 100644 --- a/src/addon/calendar/pages/event/event.html +++ b/src/addon/calendar/pages/event/event.html @@ -1,13 +1,27 @@ + + + + + + + + + - + + + + {{ 'core.hasdatatosync' | translate:{$a: 'addon.calendar.calendarevent' | translate} }} + + diff --git a/src/addon/calendar/pages/event/event.ts b/src/addon/calendar/pages/event/event.ts index 77d4bce09..6fb0a16d1 100644 --- a/src/addon/calendar/pages/event/event.ts +++ b/src/addon/calendar/pages/event/event.ts @@ -12,12 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, ViewChild } from '@angular/core'; -import { IonicPage, Content, NavParams } from 'ionic-angular'; +import { Component, ViewChild, Optional, OnDestroy, NgZone } from '@angular/core'; +import { IonicPage, Content, NavParams, NavController } from 'ionic-angular'; import { TranslateService } from '@ngx-translate/core'; import { AddonCalendarProvider } from '../../providers/calendar'; import { AddonCalendarHelperProvider } from '../../providers/helper'; +import { AddonCalendarOfflineProvider } from '../../providers/calendar-offline'; +import { AddonCalendarSyncProvider } from '../../providers/calendar-sync'; import { CoreCoursesProvider } from '@core/courses/providers/courses'; +import { CoreAppProvider } from '@providers/app'; +import { CoreEventsProvider } from '@providers/events'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreSitesProvider } from '@providers/sites'; @@ -25,6 +29,8 @@ import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreGroupsProvider } from '@providers/groups'; +import { CoreSplitViewComponent } from '@components/split-view/split-view'; +import { Network } from '@ionic-native/network'; /** * Page that displays a single calendar event. @@ -34,11 +40,17 @@ import { CoreGroupsProvider } from '@providers/groups'; selector: 'page-addon-calendar-event', templateUrl: 'event.html', }) -export class AddonCalendarEventPage { +export class AddonCalendarEventPage implements OnDestroy { @ViewChild(Content) content: Content; protected eventId; protected siteHomeId: number; + protected editEventObserver: any; + protected syncObserver: any; + protected manualSyncObserver: any; + protected onlineObserver: any; + protected currentSiteId: string; + eventLoaded: boolean; notificationFormat: string; notificationMin: string; @@ -55,17 +67,31 @@ export class AddonCalendarEventPage { currentTime: number; defaultTime: number; reminders: any[]; + canEdit = false; + hasOffline = false; + isOnline = false; + syncIcon: string; // Sync icon. + isSplitViewOn = false; constructor(private translate: TranslateService, private calendarProvider: AddonCalendarProvider, navParams: NavParams, private domUtils: CoreDomUtilsProvider, private coursesProvider: CoreCoursesProvider, private calendarHelper: AddonCalendarHelperProvider, private sitesProvider: CoreSitesProvider, localNotificationsProvider: CoreLocalNotificationsProvider, private courseProvider: CoreCourseProvider, private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider, - private groupsProvider: CoreGroupsProvider) { + private groupsProvider: CoreGroupsProvider, @Optional() private svComponent: CoreSplitViewComponent, + private navCtrl: NavController, private eventsProvider: CoreEventsProvider, network: Network, zone: NgZone, + private calendarSync: AddonCalendarSyncProvider, private appProvider: CoreAppProvider, + private calendarOffline: AddonCalendarOfflineProvider) { this.eventId = navParams.get('id'); this.notificationsEnabled = localNotificationsProvider.isAvailable(); this.siteHomeId = sitesProvider.getCurrentSite().getSiteHomeId(); + this.currentSiteId = sitesProvider.getCurrentSiteId(); + this.isSplitViewOn = this.svComponent && this.svComponent.isOn(); + + // Check if site supports editing. No need to check allowed types, event.canedit already does it. + this.canEdit = this.calendarProvider.canEditEventsInSite(); + if (this.notificationsEnabled) { this.calendarProvider.getEventReminders(this.eventId).then((reminders) => { this.reminders = reminders; @@ -79,34 +105,105 @@ export class AddonCalendarEventPage { this.notificationFormat = this.timeUtils.fixFormatForDatetime(this.timeUtils.convertPHPToMoment( this.translate.instant('core.strftimedatetime'))); } + + // Listen for event edited. If current event is edited, reload the data. + this.editEventObserver = eventsProvider.on(AddonCalendarProvider.EDIT_EVENT_EVENT, (data) => { + if (data && data.event && data.event.id == this.eventId) { + this.eventLoaded = false; + this.refreshEvent(true, false); + } + }, this.currentSiteId); + + // Refresh data if this calendar event is synchronized automatically. + this.syncObserver = eventsProvider.on(AddonCalendarSyncProvider.AUTO_SYNCED, this.checkSyncResult.bind(this, false), + this.currentSiteId); + + // Refresh data if calendar events are synchronized manually but not by this page. + this.manualSyncObserver = eventsProvider.on(AddonCalendarSyncProvider.MANUAL_SYNCED, this.checkSyncResult.bind(this, true), + this.currentSiteId); + + // Refresh online status when changes. + this.onlineObserver = network.onchange().subscribe((online) => { + // Execute the callback in the Angular zone, so change detection doesn't stop working. + zone.run(() => { + this.isOnline = online; + }); + }); } /** * View loaded. */ ionViewDidLoad(): void { - this.fetchEvent().finally(() => { - this.eventLoaded = true; - }); + this.syncIcon = 'spinner'; + + this.fetchEvent(); } /** * Fetches the event and updates the view. * + * @param {boolean} [sync] Whether it should try to synchronize offline events. + * @param {boolean} [showErrors] Whether to show sync errors to the user. * @return {Promise} Promise resolved when done. */ - fetchEvent(): Promise { + fetchEvent(sync?: boolean, showErrors?: boolean): Promise { const currentSite = this.sitesProvider.getCurrentSite(), canGetById = this.calendarProvider.isGetEventByIdAvailable(); let promise; - if (canGetById) { - promise = this.calendarProvider.getEventById(this.eventId); + this.isOnline = this.appProvider.isOnline(); + + if (sync) { + // Try to synchronize offline events. + promise = this.calendarSync.syncEvents().then((result) => { + if (result.warnings && result.warnings.length) { + this.domUtils.showErrorModal(result.warnings[0]); + } + + if (result.updated) { + // Trigger a manual sync event. + result.source = 'event'; + + this.eventsProvider.trigger(AddonCalendarSyncProvider.MANUAL_SYNCED, result, this.currentSiteId); + } + }).catch((error) => { + if (showErrors) { + this.domUtils.showErrorModalDefault(error, 'core.errorsync', true); + } + }); } else { - promise = this.calendarProvider.getEvent(this.eventId); + promise = Promise.resolve(); } - return promise.then((event) => { + return promise.then(() => { + const promises = []; + + // Get the event data. + if (canGetById) { + promises.push(this.calendarProvider.getEventById(this.eventId)); + } else { + promises.push(this.calendarProvider.getEvent(this.eventId)); + } + + // Get offline data. + promises.push(this.calendarOffline.getEvent(this.eventId).catch(() => { + // No offline data. + })); + + return Promise.all(promises).then((results) => { + if (results[1]) { + // There is offline data, apply it. + this.hasOffline = true; + Object.assign(results[0], results[1]); + } else { + this.hasOffline = false; + } + + return results[0]; + }); + + }).then((event) => { const promises = []; this.calendarHelper.formatEventData(event); @@ -196,6 +293,9 @@ export class AddonCalendarEventPage { return Promise.all(promises); }).catch((error) => { this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevent', true); + }).finally(() => { + this.eventLoaded = true; + this.syncIcon = 'sync'; }); } @@ -246,16 +346,77 @@ export class AddonCalendarEventPage { }); } + /** + * Refresh the data. + * + * @param {any} [refresher] Refresher. + * @param {Function} [done] Function to call when done. + * @param {boolean} [showErrors] Whether to show sync errors to the user. + * @return {Promise} Promise resolved when done. + */ + doRefresh(refresher?: any, done?: () => void, showErrors?: boolean): Promise { + if (this.eventLoaded) { + return this.refreshEvent(true, showErrors).finally(() => { + refresher && refresher.complete(); + done && done(); + }); + } + + return Promise.resolve(); + } + /** * Refresh the event. * - * @param {any} refresher Refresher. + * @param {boolean} [sync] Whether it should try to synchronize offline events. + * @param {boolean} [showErrors] Whether to show sync errors to the user. + * @return {Promise} Promise resolved when done. */ - refreshEvent(refresher: any): void { - this.calendarProvider.invalidateEvent(this.eventId).finally(() => { - this.fetchEvent().finally(() => { - refresher.complete(); - }); + refreshEvent(sync?: boolean, showErrors?: boolean): Promise { + this.syncIcon = 'spinner'; + + return this.calendarProvider.invalidateEvent(this.eventId).catch(() => { + // Ignore errors. + }).then(() => { + return this.fetchEvent(sync, showErrors); }); } + + /** + * Open the page to edit the event. + */ + openEdit(): void { + // Decide which navCtrl to use. If this page is inside a split view, use the split view's master nav. + const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl; + navCtrl.push('AddonCalendarEditEventPage', {eventId: this.eventId}); + } + + /** + * Check the result of an automatic sync or a manual sync not done by this page. + * + * @param {boolean} isManual Whether it's a manual sync. + * @param {any} data Sync result. + */ + protected checkSyncResult(isManual: boolean, data: any): void { + if (data && data.events && (!isManual || data.source != 'event')) { + const event = data.events.find((ev) => { + return ev.id == this.eventId; + }); + + if (event) { + this.eventLoaded = false; + this.refreshEvent(); + } + } + } + + /** + * Page destroyed. + */ + ngOnDestroy(): void { + this.editEventObserver && this.editEventObserver.off(); + this.syncObserver && this.syncObserver.off(); + this.manualSyncObserver && this.manualSyncObserver.off(); + this.onlineObserver && this.onlineObserver.unsubscribe(); + } } diff --git a/src/addon/calendar/pages/list/list.html b/src/addon/calendar/pages/list/list.html index 0204b9acf..57563190d 100644 --- a/src/addon/calendar/pages/list/list.html +++ b/src/addon/calendar/pages/list/list.html @@ -7,7 +7,7 @@ - + diff --git a/src/addon/calendar/pages/list/list.ts b/src/addon/calendar/pages/list/list.ts index 1a57c4c0b..3418f0514 100644 --- a/src/addon/calendar/pages/list/list.ts +++ b/src/addon/calendar/pages/list/list.ts @@ -59,8 +59,11 @@ export class AddonCalendarListPage implements OnDestroy { protected preSelectedCourseId: number; protected newEventObserver: any; protected discardedObserver: any; + protected editEventObserver: any; protected syncObserver: any; + protected manualSyncObserver: any; protected onlineObserver: any; + protected currentSiteId: string; courses: any[]; eventsLoaded = false; @@ -80,7 +83,7 @@ export class AddonCalendarListPage implements OnDestroy { constructor(private translate: TranslateService, private calendarProvider: AddonCalendarProvider, navParams: NavParams, private domUtils: CoreDomUtilsProvider, private coursesProvider: CoreCoursesProvider, private utils: CoreUtilsProvider, - private calendarHelper: AddonCalendarHelperProvider, private sitesProvider: CoreSitesProvider, zone: NgZone, + private calendarHelper: AddonCalendarHelperProvider, sitesProvider: CoreSitesProvider, zone: NgZone, localNotificationsProvider: CoreLocalNotificationsProvider, private popoverCtrl: PopoverController, private eventsProvider: CoreEventsProvider, private navCtrl: NavController, private appProvider: CoreAppProvider, private calendarOffline: AddonCalendarOfflineProvider, private calendarSync: AddonCalendarSyncProvider, @@ -88,12 +91,13 @@ export class AddonCalendarListPage implements OnDestroy { this.siteHomeId = sitesProvider.getCurrentSite().getSiteHomeId(); this.notificationsEnabled = localNotificationsProvider.isAvailable(); + this.currentSiteId = sitesProvider.getCurrentSiteId(); if (this.notificationsEnabled) { // Re-schedule events if default time changes. this.obsDefaultTimeChange = eventsProvider.on(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, () => { calendarProvider.scheduleEventsNotifications(this.events); - }, sitesProvider.getCurrentSiteId()); + }, this.currentSiteId); } this.eventId = navParams.get('eventId') || false; @@ -116,7 +120,7 @@ export class AddonCalendarListPage implements OnDestroy { } }); } - }, sitesProvider.getCurrentSiteId()); + }, this.currentSiteId); // Listen for new event discarded event. When it does, reload the data. this.discardedObserver = eventsProvider.on(AddonCalendarProvider.NEW_EVENT_DISCARDED_EVENT, () => { @@ -127,13 +131,29 @@ export class AddonCalendarListPage implements OnDestroy { this.eventsLoaded = false; this.refreshEvents(true, false); - }, sitesProvider.getCurrentSiteId()); + }, this.currentSiteId); + + // Listen for events edited. When an event is edited, reload the data. + this.editEventObserver = eventsProvider.on(AddonCalendarProvider.EDIT_EVENT_EVENT, (data) => { + if (data && data.event) { + this.eventsLoaded = false; + this.refreshEvents(true, false); + } + }, this.currentSiteId); // Refresh data if calendar events are synchronized automatically. this.syncObserver = eventsProvider.on(AddonCalendarSyncProvider.AUTO_SYNCED, (data) => { this.eventsLoaded = false; this.refreshEvents(); - }, sitesProvider.getCurrentSiteId()); + }, this.currentSiteId); + + // Refresh data if calendar events are synchronized manually but not by this page. + this.manualSyncObserver = eventsProvider.on(AddonCalendarSyncProvider.MANUAL_SYNCED, (data) => { + if (data && data.source != 'list') { + this.eventsLoaded = false; + this.refreshEvents(); + } + }, this.currentSiteId); // Refresh online status when changes. this.onlineObserver = network.onchange().subscribe((online) => { @@ -187,9 +207,9 @@ export class AddonCalendarListPage implements OnDestroy { if (result.updated) { // Trigger a manual sync event. - this.eventsProvider.trigger(AddonCalendarSyncProvider.MANUAL_SYNCED, { - source: 'list' - }, this.sitesProvider.getCurrentSiteId()); + result.source = 'list'; + + this.eventsProvider.trigger(AddonCalendarSyncProvider.MANUAL_SYNCED, result, this.currentSiteId); } }).catch((error) => { if (showErrors) { @@ -530,6 +550,8 @@ export class AddonCalendarListPage implements OnDestroy { * @param {number} [eventId] Event ID to edit. */ openEdit(eventId?: number): void { + this.eventId = undefined; + const params: any = {}; if (eventId) { @@ -574,7 +596,9 @@ export class AddonCalendarListPage implements OnDestroy { this.obsDefaultTimeChange && this.obsDefaultTimeChange.off(); this.newEventObserver && this.newEventObserver.off(); this.discardedObserver && this.discardedObserver.off(); + this.editEventObserver && this.editEventObserver.off(); this.syncObserver && this.syncObserver.off(); - this.onlineObserver && this.onlineObserver.off(); + this.manualSyncObserver && this.manualSyncObserver.off(); + this.onlineObserver && this.onlineObserver.unsubscribe(); } } diff --git a/src/addon/calendar/providers/calendar-sync.ts b/src/addon/calendar/providers/calendar-sync.ts index cb9eb4b63..834d1ac34 100644 --- a/src/addon/calendar/providers/calendar-sync.ts +++ b/src/addon/calendar/providers/calendar-sync.ts @@ -37,8 +37,6 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { static MANUAL_SYNCED = 'addon_calendar_manual_synced'; static SYNC_ID = 'calendar'; - protected componentTranslate: string; - constructor(translate: TranslateService, appProvider: CoreAppProvider, courseProvider: CoreCourseProvider, @@ -54,8 +52,6 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { super('AddonCalendarSyncProvider', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, timeUtils); - - this.componentTranslate = this.translate.instant('addon.calendar.calendarevent'); } /** @@ -66,7 +62,7 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ syncAllEvents(siteId?: string, force?: boolean): Promise { - return this.syncOnSites('all calendars', this.syncAllEventsFunc.bind(this), [force], siteId); + return this.syncOnSites('all calendar events', this.syncAllEventsFunc.bind(this), [force], siteId); } /** @@ -77,6 +73,7 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { * @return {Promise} Promise resolved if sync is successful, rejected if sync fails. */ protected syncAllEventsFunc(siteId: string, force?: boolean): Promise { + const promise = force ? this.syncEvents(siteId) : this.syncEventsIfNeeded(siteId); return promise.then((result) => { @@ -196,7 +193,8 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { if (this.syncProvider.isBlocked(AddonCalendarProvider.COMPONENT, event.id, siteId)) { this.logger.debug('Cannot sync event ' + event.name + ' because it is blocked.'); - return Promise.reject(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate})); + return Promise.reject(this.translate.instant('core.errorsyncblocked', + {$a: this.translate.instant('addon.calendar.calendarevent')})); } // Try to send the data. @@ -216,7 +214,7 @@ export class AddonCalendarSyncProvider extends CoreSyncBaseProvider { return this.calendarOffline.deleteEvent(event.id, siteId).then(() => { // Event deleted, add a warning. result.warnings.push(this.translate.instant('core.warningofflinedatadeleted', { - component: this.componentTranslate, + component: this.translate.instant('addon.calendar.calendarevent'), name: event.name, error: this.textUtils.getErrorMessageFromError(error) })); diff --git a/src/addon/calendar/providers/calendar.ts b/src/addon/calendar/providers/calendar.ts index 6e5bc965b..0b9af1e6b 100644 --- a/src/addon/calendar/providers/calendar.ts +++ b/src/addon/calendar/providers/calendar.ts @@ -27,6 +27,7 @@ import { CoreConfigProvider } from '@providers/config'; import { ILocalNotification } from '@ionic-native/local-notifications'; import { SQLiteDB } from '@classes/sqlitedb'; import { AddonCalendarOfflineProvider } from './calendar-offline'; +import { TranslateService } from '@ngx-translate/core'; /** * Service to handle calendar events. @@ -40,6 +41,7 @@ export class AddonCalendarProvider { static DEFAULT_NOTIFICATION_TIME = 60; static NEW_EVENT_EVENT = 'addon_calendar_new_event'; static NEW_EVENT_DISCARDED_EVENT = 'addon_calendar_new_event_discarded'; + static EDIT_EVENT_EVENT = 'addon_calendar_edit_event'; static TYPE_CATEGORY = 'category'; static TYPE_COURSE = 'course'; static TYPE_GROUP = 'group'; @@ -218,7 +220,7 @@ export class AddonCalendarProvider { private coursesProvider: CoreCoursesProvider, private timeUtils: CoreTimeUtilsProvider, private localNotificationsProvider: CoreLocalNotificationsProvider, private configProvider: CoreConfigProvider, private utils: CoreUtilsProvider, private calendarOffline: AddonCalendarOfflineProvider, - private appProvider: CoreAppProvider) { + private appProvider: CoreAppProvider, private translate: TranslateService) { this.logger = logger.getInstance('AddonCalendarProvider'); this.sitesProvider.registerSiteSchema(this.siteSchema); } @@ -980,7 +982,12 @@ export class AddonCalendarProvider { formData.userid = site.getUserId(); formData.visible = 1; formData.instance = 0; - formData['_qf__core_calendar_local_event_forms_create'] = 1; + + if (eventId > 0) { + formData['_qf__core_calendar_local_event_forms_update'] = 1; + } else { + formData['_qf__core_calendar_local_event_forms_create'] = 1; + } const params = { formdata: this.utils.objectToGetParams(formData) @@ -988,7 +995,11 @@ export class AddonCalendarProvider { return site.write('core_calendar_submit_create_update_form', params).then((result) => { if (result.validationerror) { - return Promise.reject(this.utils.createFakeWSError('')); + // Simulate a WS error. + return Promise.reject({ + message: this.translate.instant('core.invalidformdata'), + errorcode: 'validationerror' + }); } return result.event; diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index a7884b46f..e8a1c8227 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -1484,6 +1484,7 @@ "core.image": "Image", "core.imageviewer": "Image viewer", "core.info": "Information", + "core.invalidformdata": "Incorrect form data", "core.ios": "iOS", "core.labelsep": ":", "core.lastaccess": "Last access", diff --git a/src/lang/en.json b/src/lang/en.json index b5c79cb94..4ec6a60a4 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -117,6 +117,7 @@ "image": "Image", "imageviewer": "Image viewer", "info": "Information", + "invalidformdata": "Incorrect form data", "ios": "iOS", "labelsep": ":", "lastaccess": "Last access", diff --git a/src/providers/utils/utils.ts b/src/providers/utils/utils.ts index 0f9f71b3b..95cd5cee1 100644 --- a/src/providers/utils/utils.ts +++ b/src/providers/utils/utils.ts @@ -1058,7 +1058,7 @@ export class CoreUtilsProvider { * Convert an object to a format of GET param. E.g.: {a: 1, b: 2} -> a=1&b=2 * * @param {any} object Object to convert. - * @param {boolean} [removeEmpty=true] Whether to remove params whose value is empty/null/undefined. + * @param {boolean} [removeEmpty=true] Whether to remove params whose value is null/undefined. * @return {string} GET params. */ objectToGetParams(object: any, removeEmpty: boolean = true): string { @@ -1070,7 +1070,7 @@ export class CoreUtilsProvider { for (const name in flattened) { let value = flattened[name]; - if (removeEmpty && (value === null || typeof value == 'undefined' || value === '')) { + if (removeEmpty && (value === null || typeof value == 'undefined')) { continue; }