-
{{ 'addon.calendar.eventduration' | translate }}
+
+ {{ 'addon.calendar.eventduration' | translate }}
{{ 'addon.calendar.durationnone' | translate }}
@@ -118,15 +118,30 @@
-
-
- {{ 'addon.calendar.repeatevent' | translate }}
-
-
-
- {{ 'addon.calendar.repeatweeksl' | translate }}
-
-
+
+
+
+ {{ 'addon.calendar.repeatevent' | translate }}
+
+
+
+ {{ 'addon.calendar.repeatweeksl' | translate }}
+
+
+
+
+
+
+ {{ 'addon.calendar.repeatedevents' | translate }}
+
+ {{ 'addon.calendar.repeateditall' | translate:{$a: event.othereventscount} }}
+
+
+
+ {{ 'addon.calendar.repeateditthis' | translate }}
+
+
+
@@ -134,7 +149,7 @@
-
+
diff --git a/src/addon/calendar/pages/edit-event/edit-event.scss b/src/addon/calendar/pages/edit-event/edit-event.scss
index 3c43c635e..6426ce3f1 100644
--- a/src/addon/calendar/pages/edit-event/edit-event.scss
+++ b/src/addon/calendar/pages/edit-event/edit-event.scss
@@ -1,5 +1,5 @@
ion-app.app-root page-addon-calendar-edit-event {
- .addon-calendar-duration-container ion-item:not(.addon-calendar-duration-title) {
+ .addon-calendar-radio-container ion-item:not(.addon-calendar-radio-title) {
&.item-ios {
@include padding-horizontal($item-ios-padding-start * 2, null);
diff --git a/src/addon/calendar/pages/edit-event/edit-event.ts b/src/addon/calendar/pages/edit-event/edit-event.ts
index bf394c205..edda98102 100644
--- a/src/addon/calendar/pages/edit-event/edit-event.ts
+++ b/src/addon/calendar/pages/edit-event/edit-event.ts
@@ -21,6 +21,7 @@ import { CoreGroupsProvider } from '@providers/groups';
import { CoreSitesProvider } from '@providers/sites';
import { CoreSyncProvider } from '@providers/sync';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
+import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreTimeUtilsProvider } from '@providers/utils/time';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreCoursesProvider } from '@core/courses/providers/courses';
@@ -57,6 +58,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
courseGroupSet = false;
advanced = false;
errors: any;
+ event: any; // The event object (when editing an event).
// Form variables.
eventForm: FormGroup;
@@ -71,11 +73,14 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
protected types: any; // Object with the supported types.
protected showAll: boolean;
protected isDestroyed = false;
+ protected error = false;
+ protected gotEventData = false;
constructor(navParams: NavParams,
private navCtrl: NavController,
private translate: TranslateService,
private domUtils: CoreDomUtilsProvider,
+ private textUtils: CoreTextUtilsProvider,
private timeUtils: CoreTimeUtilsProvider,
private eventsProvider: CoreEventsProvider,
private groupsProvider: CoreGroupsProvider,
@@ -125,6 +130,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
this.eventForm.addControl('timedurationminutes', this.fb.control(''));
this.eventForm.addControl('repeat', this.fb.control(false));
this.eventForm.addControl('repeats', this.fb.control('1'));
+ this.eventForm.addControl('repeateditall', this.fb.control(1));
}
/**
@@ -146,6 +152,8 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
protected fetchData(refresh?: boolean): Promise {
let accessInfo;
+ this.error = false;
+
// Get access info.
return this.calendarProvider.getAccessInformation(this.courseId).then((info) => {
accessInfo = info;
@@ -161,8 +169,8 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
return Promise.reject(this.translate.instant('addon.calendar.nopermissiontoupdatecalendar'));
}
- if (this.eventId && !refresh) {
- // If editing an event, get offline data. Wait for sync first.
+ if (this.eventId && !this.gotEventData) {
+ // 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 +178,39 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
this.syncProvider.blockOperation(AddonCalendarProvider.COMPONENT, this.eventId);
}
- // Get the event data if there's any.
- return this.calendarOffline.getEvent(this.eventId).then((event) => {
+ const promises = [];
+
+ // Get the event offline data if there's any.
+ promises.push(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.
+ promises.push(this.calendarProvider.getEventById(this.eventId).then((event) => {
+ this.event = event;
+ if (event && event.repeatid) {
+ event.othereventscount = event.eventcount ? event.eventcount - 1 : '';
+ }
+
+ return event;
+ }));
+ }
+
+ return Promise.all(promises).then((result) => {
+ this.gotEventData = true;
+
+ const event = result[0] || result[1]; // Use offline data first.
+
+ if (event) {
+ // Load the data in the form.
+ return this.loadEventData(event, !!result[0]);
+ }
});
}));
}
@@ -220,12 +238,24 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
});
}
- // Sort courses by name.
- this.courses = courses.sort((a, b) => {
- const compareA = a.fullname.toLowerCase(),
- compareB = b.fullname.toLowerCase();
+ // Format the name of the courses.
+ const subPromises = [];
+ courses.forEach((course) => {
+ subPromises.push(this.textUtils.formatText(course.fullname).then((text) => {
+ course.fullname = text;
+ }).catch(() => {
+ // Ignore errors.
+ }));
+ });
- return compareA.localeCompare(compareB);
+ return Promise.all(subPromises).then(() => {
+ // Sort courses by name.
+ this.courses = courses.sort((a, b) => {
+ const compareA = a.fullname.toLowerCase(),
+ compareB = b.fullname.toLowerCase();
+
+ return compareA.localeCompare(compareB);
+ });
});
}));
}
@@ -245,11 +275,70 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'Error getting data.');
- this.originalData = null; // Avoid asking for confirmation.
- this.navCtrl.pop();
+ this.error = true;
+
+ if (!this.svComponent || !this.svComponent.isOn()) {
+ this.originalData = null; // Avoid asking for confirmation.
+ this.navCtrl.pop();
+ }
});
}
+ /**
+ * Load an event data into the form.
+ *
+ * @param {any} event Event data.
+ * @param {boolean} isOffline Whether the data is from offline or not.
+ * @return {Promise} Promise resolved when done.
+ */
+ protected loadEventData(event: any, isOffline: boolean): Promise {
+ const courseId = event.course ? event.course.id : event.courseid;
+
+ 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(courseId || '');
+ this.eventForm.controls.groupcourseid.setValue(event.groupcourseid || courseId || '');
+ this.eventForm.controls.groupid.setValue(event.groupid || '');
+ this.eventForm.controls.description.setValue(event.description);
+ this.eventForm.controls.location.setValue(event.location);
+
+ if (isOffline) {
+ // It's an offline event, use the data as it is.
+ 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');
+ this.eventForm.controls.repeateditall.setValue(event.repeateditall || 1);
+ } else {
+ // Online event, we'll have to calculate the data.
+
+ if (event.timeduration > 0) {
+ this.eventForm.controls.duration.setValue(1);
+ this.eventForm.controls.timedurationuntil.setValue(this.timeUtils.toDatetimeFormat(
+ (event.timestart + event.timeduration) * 1000));
+ } else {
+ // No duration.
+ this.eventForm.controls.duration.setValue(0);
+ this.eventForm.controls.timedurationuntil.setValue(this.timeUtils.toDatetimeFormat());
+ }
+
+ this.eventForm.controls.timedurationminutes.setValue('');
+ this.eventForm.controls.repeat.setValue(!!event.repeatid);
+ this.eventForm.controls.repeats.setValue(event.eventcount || '1');
+ this.eventForm.controls.repeateditall.setValue(1);
+ }
+
+ if (event.eventtype == 'group' && courseId) {
+ return this.loadGroups(courseId);
+ }
+
+ return Promise.resolve();
+ }
+
/**
* Pull to refresh.
*
@@ -292,20 +381,33 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
}
const modal = this.domUtils.showModalLoading();
- this.loadingGroups = true;
- this.groupsProvider.getUserGroupsInCourse(courseId).then((groups) => {
- this.groups = groups;
- this.courseGroupSet = true;
+ this.loadGroups(courseId).then(() => {
this.groupControl.setValue('');
}).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'Error getting data.');
}).finally(() => {
- this.loadingGroups = false;
modal.dismiss();
});
}
+ /**
+ * Load groups of a certain course.
+ *
+ * @param {number} courseId Course ID.
+ * @return {Promise} Promise resolved when done.
+ */
+ protected loadGroups(courseId: number): Promise {
+ this.loadingGroups = true;
+
+ return this.groupsProvider.getUserGroupsInCourse(courseId).then((groups) => {
+ this.groups = groups;
+ this.courseGroupSet = true;
+ }).finally(() => {
+ this.loadingGroups = false;
+ });
+ }
+
/**
* Show or hide advanced form fields.
*/
@@ -378,8 +480,13 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy {
data.repeats = formData.repeats;
}
+ if (this.event && this.event.repeatid) {
+ data.repeatid = this.event.repeatid;
+ data.repeateditall = formData.repeateditall;
+ }
+
// 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 +506,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..2608581ae 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} }}
+
+
@@ -26,10 +40,10 @@
{{ 'core.course' | translate}}
-
+
{{ 'core.group' | translate}}
{{ groupName }}
-
+
{{ 'core.category' | 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..a224599f7 100644
--- a/src/addon/calendar/pages/list/list.html
+++ b/src/addon/calendar/pages/list/list.html
@@ -7,7 +7,7 @@
-
+
@@ -26,20 +26,6 @@
-
-
- {{ 'core.notsent' | translate }}
-
-
-
-
- {{ event.timestart * 1000 | coreFormatDate: "strftimedatetimeshort" }}
- - {{ (event.timestart + event.timeduration) * 1000 | coreFormatDate: "strftimedatetimeshort" }}
-
-
-
-
-
@@ -54,6 +40,10 @@
- {{ (event.timestart + event.timeduration) * 1000 | coreFormatDate: "strftimetime" }}
- {{ (event.timestart + event.timeduration) * 1000 | coreFormatDate: "strftimedatetimeshort" }}
+
+
+ {{ 'core.notsent' | translate }}
+
diff --git a/src/addon/calendar/pages/list/list.ts b/src/addon/calendar/pages/list/list.ts
index 1a57c4c0b..e3ce88a44 100644
--- a/src/addon/calendar/pages/list/list.ts
+++ b/src/addon/calendar/pages/list/list.ts
@@ -21,6 +21,7 @@ import { AddonCalendarHelperProvider } from '../../providers/helper';
import { AddonCalendarSyncProvider } from '../../providers/calendar-sync';
import { CoreCoursesProvider } from '@core/courses/providers/courses';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
+import { CoreTimeUtilsProvider } from '@providers/utils/time';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreSitesProvider } from '@providers/sites';
import { CoreLocalNotificationsProvider } from '@providers/local-notifications';
@@ -30,6 +31,7 @@ import { CoreAppProvider } from '@providers/app';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
import * as moment from 'moment';
import { Network } from '@ionic-native/network';
+import { CoreConstants } from '@core/constants';
/**
* Page that displays the list of calendar events.
@@ -43,6 +45,7 @@ export class AddonCalendarListPage implements OnDestroy {
@ViewChild(Content) content: Content;
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
+ protected initialTime = 0;
protected daysLoaded = 0;
protected emptyEventsTimes = 0; // Variable to identify consecutive calls returning 0 events.
protected categoriesRetrieved = false;
@@ -59,12 +62,16 @@ 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;
- events = [];
+ events = []; // Events (both online and offline).
+ onlineEvents = [];
offlineEvents = [];
notificationsEnabled = false;
filteredEvents = [];
@@ -80,20 +87,21 @@ 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,
- network: Network) {
+ network: Network, private timeUtils: CoreTimeUtilsProvider) {
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());
+ calendarProvider.scheduleEventsNotifications(this.onlineEvents);
+ }, this.currentSiteId);
}
this.eventId = navParams.get('eventId') || false;
@@ -116,7 +124,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 +135,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) => {
@@ -157,8 +181,12 @@ export class AddonCalendarListPage implements OnDestroy {
this.fetchData(false, true, false).then(() => {
if (!this.eventId && this.splitviewCtrl.isOn() && this.events.length > 0) {
- // Take first and load it.
- this.gotoEvent(this.events[0].id);
+ // Take first online event and load it. If no online event, load the first offline.
+ if (this.onlineEvents[0]) {
+ this.gotoEvent(this.onlineEvents[0].id);
+ } else {
+ this.gotoEvent(this.offlineEvents[0].id);
+ }
}
});
}
@@ -172,6 +200,7 @@ export class AddonCalendarListPage implements OnDestroy {
* @return {Promise} Promise resolved when done.
*/
fetchData(refresh?: boolean, sync?: boolean, showErrors?: boolean): Promise {
+ this.initialTime = this.timeUtils.timestamp();
this.daysLoaded = 0;
this.emptyEventsTimes = 0;
this.isOnline = this.appProvider.isOnline();
@@ -187,9 +216,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) {
@@ -229,8 +258,11 @@ export class AddonCalendarListPage implements OnDestroy {
this.hasOffline = !!events.length;
// Format data and sort by timestart.
- events.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper));
- this.offlineEvents = events.sort((a, b) => a.timestart - b.timestart);
+ events.forEach((event) => {
+ event.offline = true;
+ this.calendarHelper.formatEventData(event);
+ });
+ this.offlineEvents = this.sortEvents(events);
}));
return Promise.all(promises);
@@ -249,38 +281,38 @@ export class AddonCalendarListPage implements OnDestroy {
fetchEvents(refresh?: boolean): Promise {
this.loadMoreError = false;
- return this.calendarProvider.getEventsList(this.daysLoaded, AddonCalendarProvider.DAYS_INTERVAL).then((events) => {
- this.daysLoaded += AddonCalendarProvider.DAYS_INTERVAL;
- if (events.length === 0) {
+ return this.calendarProvider.getEventsList(this.initialTime, this.daysLoaded, AddonCalendarProvider.DAYS_INTERVAL)
+ .then((onlineEvents) => {
+
+ if (onlineEvents.length === 0) {
this.emptyEventsTimes++;
if (this.emptyEventsTimes > 5) { // Stop execution if we retrieve empty list 6 consecutive times.
this.canLoadMore = false;
if (refresh) {
- this.events = [];
+ this.onlineEvents = [];
this.filteredEvents = [];
+ this.events = this.offlineEvents;
}
} else {
// No events returned, load next events.
+ this.daysLoaded += AddonCalendarProvider.DAYS_INTERVAL;
+
return this.fetchEvents();
}
} else {
- events.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper));
+ onlineEvents.forEach(this.calendarHelper.formatEventData.bind(this.calendarHelper));
- // Sort the events by timestart, they're ordered by id.
- events.sort((a, b) => {
- if (a.timestart == b.timestart) {
- return a.timeduration - b.timeduration;
- }
+ // Get the merged events of this period.
+ const events = this.mergeEvents(onlineEvents);
- return a.timestart - b.timestart;
- });
-
- this.getCategories = this.shouldLoadCategories(events);
+ this.getCategories = this.shouldLoadCategories(onlineEvents);
if (refresh) {
+ this.onlineEvents = onlineEvents;
this.events = events;
} else {
// Filter events with same ID. Repeated events are returned once per WS call, show them only once.
+ this.onlineEvents = this.utils.mergeArraysWithoutDuplicates(this.onlineEvents, onlineEvents, 'id');
this.events = this.utils.mergeArraysWithoutDuplicates(this.events, events, 'id');
}
this.filteredEvents = this.getFilteredEvents();
@@ -293,7 +325,9 @@ export class AddonCalendarListPage implements OnDestroy {
this.canLoadMore = true;
// Schedule notifications for the events retrieved (might have new events).
- this.calendarProvider.scheduleEventsNotifications(this.events);
+ this.calendarProvider.scheduleEventsNotifications(this.onlineEvents);
+
+ this.daysLoaded += AddonCalendarProvider.DAYS_INTERVAL;
}
// Resize the content so infinite loading is able to calculate if it should load more items or not.
@@ -416,6 +450,61 @@ export class AddonCalendarListPage implements OnDestroy {
});
}
+ /**
+ * Merge a period of online events with the offline events of that period.
+ *
+ * @param {any[]} onlineEvents Online events.
+ * @return {any[]} Merged events.
+ */
+ protected mergeEvents(onlineEvents: any[]): any[] {
+ if (!this.offlineEvents || !this.offlineEvents.length) {
+ // No offline events, nothing to merge.
+ return onlineEvents;
+ }
+
+ const start = this.initialTime + (CoreConstants.SECONDS_DAY * this.daysLoaded),
+ end = start + (CoreConstants.SECONDS_DAY * AddonCalendarProvider.DAYS_INTERVAL) - 1;
+
+ // First of all, remove the online events that were modified in offline.
+ let result = onlineEvents.filter((event) => {
+ const offlineEvent = this.offlineEvents.find((ev) => {
+ return ev.id == event.id;
+ });
+
+ return !offlineEvent;
+ });
+
+ // Now get the offline events that belong to this period.
+ const periodOfflineEvents = this.offlineEvents.filter((event) => {
+ if (this.daysLoaded == 0 && event.timestart < start) {
+ // Display offline events that are previous to current time to allow editing them.
+ return true;
+ }
+
+ return (event.timestart >= start || event.timestart + event.timeduration >= start) && event.timestart <= end;
+ });
+
+ // Merge both arrays and sort them.
+ result = result.concat(periodOfflineEvents);
+
+ return this.sortEvents(result);
+ }
+
+ /**
+ * Sort events by timestart.
+ *
+ * @param {any[]} events List to sort.
+ */
+ protected sortEvents(events: any[]): any[] {
+ return events.sort((a, b) => {
+ if (a.timestart == b.timestart) {
+ return a.timeduration - b.timeduration;
+ }
+
+ return a.timestart - b.timestart;
+ });
+ }
+
/**
* Refresh the data.
*
@@ -530,6 +619,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 +665,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-offline.ts b/src/addon/calendar/providers/calendar-offline.ts
index 833ca7d4b..a929113bc 100644
--- a/src/addon/calendar/providers/calendar-offline.ts
+++ b/src/addon/calendar/providers/calendar-offline.ts
@@ -94,6 +94,14 @@ export class AddonCalendarOfflineProvider {
name: 'repeats',
type: 'TEXT',
},
+ {
+ name: 'repeatid',
+ type: 'INTEGER',
+ },
+ {
+ name: 'repeateditall',
+ type: 'INTEGER',
+ },
{
name: 'userid',
type: 'INTEGER',
@@ -202,6 +210,8 @@ export class AddonCalendarOfflineProvider {
timedurationminutes: data.timedurationminutes,
repeat: data.repeat ? 1 : 0,
repeats: data.repeats,
+ repeatid: data.repeatid,
+ repeateditall: data.repeateditall ? 1 : 0,
timecreated: timeCreated,
userid: site.getUserId()
};
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..bfd3a03df 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);
}
@@ -535,16 +537,20 @@ export class AddonCalendarProvider {
* Get the events in a certain period. The period is calculated like this:
* start time: now + daysToStart
* end time: start time + daysInterval
- * E.g. using provider.getEventsList(30, 30) is going to get the events starting after 30 days from now
+ * E.g. using provider.getEventsList(undefined, 30, 30) is going to get the events starting after 30 days from now
* and ending before 60 days from now.
*
- * @param {number} [daysToStart=0] Number of days from now to start getting events.
+ * @param {number} [initialTime] Timestamp when the first fetch was done. If not defined, current time.
+ * @param {number} [daysToStart=0] Number of days from now to start getting events.
* @param {number} [daysInterval=30] Number of days between timestart and timeend.
* @param {string} [siteId] Site to get the events from. If not defined, use current site.
* @return {Promise} Promise to be resolved when the participants are retrieved.
*/
- getEventsList(daysToStart: number = 0, daysInterval: number = AddonCalendarProvider.DAYS_INTERVAL, siteId?: string)
- : Promise {
+ getEventsList(initialTime?: number, daysToStart: number = 0, daysInterval: number = AddonCalendarProvider.DAYS_INTERVAL,
+ siteId?: string): Promise {
+
+ initialTime = initialTime || this.timeUtils.timestamp();
+
return this.sitesProvider.getSite(siteId).then((site) => {
siteId = site.getId();
const promises = [];
@@ -559,9 +565,8 @@ export class AddonCalendarProvider {
}));
return Promise.all(promises).then(() => {
- const now = this.timeUtils.timestamp(),
- start = now + (CoreConstants.SECONDS_DAY * daysToStart),
- end = start + (CoreConstants.SECONDS_DAY * daysInterval),
+ const start = initialTime + (CoreConstants.SECONDS_DAY * daysToStart),
+ end = start + (CoreConstants.SECONDS_DAY * daysInterval) - 1,
data = {
options: {
userevents: 1,
@@ -586,6 +591,7 @@ export class AddonCalendarProvider {
const preSets = {
cacheKey: this.getEventsListCacheKey(daysToStart, daysInterval),
getCacheUsingCacheKey: true,
+ uniqueCacheKey: true,
updateFrequency: CoreSite.FREQUENCY_SOMETIMES
};
@@ -731,7 +737,7 @@ export class AddonCalendarProvider {
return this.isDisabled(siteId).then((disabled) => {
if (!disabled) {
// Get first events.
- return this.getEventsList(undefined, undefined, siteId).then((events) => {
+ return this.getEventsList(undefined, undefined, undefined, siteId).then((events) => {
return this.scheduleEventsNotifications(events, siteId);
});
}
@@ -980,7 +986,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 +999,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 f29201b76..12b6ecacf 100644
--- a/src/assets/lang/en.json
+++ b/src/assets/lang/en.json
@@ -96,6 +96,7 @@
"addon.calendar.errorloadevents": "Error loading events.",
"addon.calendar.eventduration": "Duration",
"addon.calendar.eventendtime": "End time",
+ "addon.calendar.eventkind": "Type of event",
"addon.calendar.eventname": "Event title",
"addon.calendar.eventstarttime": "Start time",
"addon.calendar.eventtype": "Event type",
@@ -106,6 +107,9 @@
"addon.calendar.noevents": "There are no events",
"addon.calendar.nopermissiontoupdatecalendar": "Sorry, but you do not have permission to update the calendar event",
"addon.calendar.reminders": "Reminders",
+ "addon.calendar.repeatedevents": "Repeated events",
+ "addon.calendar.repeateditall": "Also apply changes to the other {{$a}} events in this repeat series",
+ "addon.calendar.repeateditthis": "Apply changes to this event only",
"addon.calendar.repeatevent": "Repeat this event",
"addon.calendar.repeatweeksl": "Repeat weekly, creating altogether",
"addon.calendar.setnewreminder": "Set a new reminder",
@@ -1492,6 +1496,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 7dd93bcf8..82744f363 100644
--- a/src/lang/en.json
+++ b/src/lang/en.json
@@ -115,6 +115,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 a0c352bfd..306c71525 100644
--- a/src/providers/utils/utils.ts
+++ b/src/providers/utils/utils.ts
@@ -1085,7 +1085,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 {
@@ -1097,7 +1097,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;
}