diff --git a/config.xml b/config.xml
index f35f79a3a..33e6375ec 100644
--- a/config.xml
+++ b/config.xml
@@ -122,7 +122,7 @@
-
+
diff --git a/package.json b/package.json
index 007d42db8..b2c25cd47 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
"ionic:build:before": "gulp",
"ionic:watch:before": "gulp",
"ionic:build:after": "gulp copy-component-templates",
+ "preionic:build": "gulp",
"postionic:build": "gulp copy-component-templates",
"desktop.pack": "electron-builder --dir",
"desktop.dist": "electron-builder",
diff --git a/src/addon/mod/assign/pages/submission-list/submission-list.ts b/src/addon/mod/assign/pages/submission-list/submission-list.ts
index 3f661eb37..504a41b8d 100644
--- a/src/addon/mod/assign/pages/submission-list/submission-list.ts
+++ b/src/addon/mod/assign/pages/submission-list/submission-list.ts
@@ -92,23 +92,6 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
});
}
- /**
- * 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 split view is enabled, check if we can leave the details page.
- if (this.splitviewCtrl.isOn()) {
- const detailsPage = this.splitviewCtrl.getDetailsNav().getActive().instance;
- if (detailsPage && detailsPage.ionViewCanLeave) {
- return detailsPage.ionViewCanLeave();
- }
- }
-
- return true;
- }
-
/**
* Fetch assignment data.
*
diff --git a/src/addon/mod/data/fields/number/providers/handler.ts b/src/addon/mod/data/fields/number/providers/handler.ts
index 40663e8f0..fbf3fe68a 100644
--- a/src/addon/mod/data/fields/number/providers/handler.ts
+++ b/src/addon/mod/data/fields/number/providers/handler.ts
@@ -50,9 +50,10 @@ export class AddonModDataFieldNumberHandler extends AddonModDataFieldTextHandler
*/
hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean {
const fieldName = 'f_' + field.id,
- input = typeof inputData[fieldName] != 'undefined' ? parseFloat(inputData[fieldName]) : '';
+ input = typeof inputData[fieldName] != 'undefined' && inputData[fieldName] !== null ?
+ parseFloat(inputData[fieldName]) : '';
- originalFieldData = (originalFieldData && typeof originalFieldData.content != 'undefined') ?
+ originalFieldData = (originalFieldData && typeof originalFieldData.content != 'undefined' && originalFieldData !== null) ?
parseFloat(originalFieldData.content) : '';
return input != originalFieldData;
diff --git a/src/addon/mod/data/providers/delete-link-handler.ts b/src/addon/mod/data/providers/delete-link-handler.ts
index 9338d7817..5ba8f1b31 100644
--- a/src/addon/mod/data/providers/delete-link-handler.ts
+++ b/src/addon/mod/data/providers/delete-link-handler.ts
@@ -13,6 +13,7 @@
// limitations under the License.
import { Injectable } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
import { AddonModDataProvider } from './data';
@@ -31,7 +32,8 @@ export class AddonModDataDeleteLinkHandler extends CoreContentLinksHandlerBase {
pattern = /\/mod\/data\/view\.php.*([\?\&](d|delete)=\d+)/;
constructor(private dataProvider: AddonModDataProvider, private courseProvider: CoreCourseProvider,
- private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider) {
+ private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider,
+ private translate: TranslateService) {
super();
}
@@ -66,32 +68,37 @@ export class AddonModDataDeleteLinkHandler extends CoreContentLinksHandlerBase {
CoreContentLinksAction[] | Promise {
return [{
action: (siteId, navCtrl?): void => {
- const modal = this.domUtils.showModalLoading(),
- dataId = parseInt(params.d, 10),
- entryId = parseInt(params.delete, 10);
- this.getActivityCourseIdIfNotSet(dataId, siteId, courseId).then((cId) => {
- courseId = cId;
+ this.domUtils.showConfirm(this.translate.instant('addon.mod_data.confirmdeleterecord')).then(() => {
+ const modal = this.domUtils.showModalLoading(),
+ dataId = parseInt(params.d, 10),
+ entryId = parseInt(params.delete, 10);
- // Delete entry.
- return this.dataProvider.deleteEntry(dataId, entryId, courseId, siteId).catch((message) => {
- this.domUtils.showErrorModalDefault(message, 'addon.mod_data.errordeleting', true);
+ return this.getActivityCourseIdIfNotSet(dataId, siteId, courseId).then((cId) => {
+ courseId = cId;
- return Promise.reject(null);
+ // Delete entry.
+ return this.dataProvider.deleteEntry(dataId, entryId, courseId, siteId).catch((message) => {
+ this.domUtils.showErrorModalDefault(message, 'addon.mod_data.errordeleting', true);
+
+ return Promise.reject(null);
+ });
+ }).then(() => {
+ const promises = [];
+ promises.push(this.dataProvider.invalidateEntryData(dataId, entryId, siteId));
+ promises.push(this.dataProvider.invalidateEntriesData(dataId, siteId));
+
+ return Promise.all(promises);
+ }).then(() => {
+ this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId: dataId, entryId: entryId,
+ deleted: true}, siteId);
+
+ this.domUtils.showToast('addon.mod_data.recorddeleted', true, 3000);
+ }).finally(() => {
+ modal.dismiss();
});
- }).then(() => {
- const promises = [];
- promises.push(this.dataProvider.invalidateEntryData(dataId, entryId, siteId));
- promises.push(this.dataProvider.invalidateEntriesData(dataId, siteId));
-
- return Promise.all(promises);
- }).then(() => {
- this.eventsProvider.trigger(AddonModDataProvider.ENTRY_CHANGED, {dataId: dataId, entryId: entryId,
- deleted: true}, siteId);
-
- this.domUtils.showToast('addon.mod_data.recorddeleted', true, 3000);
- }).finally(() => {
- modal.dismiss();
+ }).catch(() => {
+ // Nothing to do.
});
}
}];
diff --git a/src/addon/mod/lti/providers/module-handler.ts b/src/addon/mod/lti/providers/module-handler.ts
index c88705911..dfd0b38b4 100644
--- a/src/addon/mod/lti/providers/module-handler.ts
+++ b/src/addon/mod/lti/providers/module-handler.ts
@@ -14,6 +14,7 @@
import { Injectable } from '@angular/core';
import { NavController, NavOptions } from 'ionic-angular';
+import { DomSanitizer } from '@angular/platform-browser';
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate';
import { CoreAppProvider } from '@providers/app';
import { CoreCourseProvider } from '@core/course/providers/course';
@@ -36,7 +37,8 @@ export class AddonModLtiModuleHandler implements CoreCourseModuleHandler {
private domUtils: CoreDomUtilsProvider,
private filepoolProvider: CoreFilepoolProvider,
private sitesProvider: CoreSitesProvider,
- private ltiProvider: AddonModLtiProvider) {}
+ private ltiProvider: AddonModLtiProvider,
+ private sanitizer: DomSanitizer) {}
/**
* Check if the handler is enabled on a site level.
@@ -100,11 +102,11 @@ export class AddonModLtiModuleHandler implements CoreCourseModuleHandler {
// Get the internal URL.
return this.filepoolProvider.getSrcByUrl(siteId, icon, AddonModLtiProvider.COMPONENT, module.id);
}).then((url) => {
- data.icon = url;
+ data.icon = this.sanitizer.bypassSecurityTrustUrl(url);
}).catch(() => {
// Error downloading. If we're online we'll set the online url.
if (this.appProvider.isOnline()) {
- data.icon = icon;
+ data.icon = this.sanitizer.bypassSecurityTrustUrl(icon);
}
});
}
diff --git a/src/addon/notifications/pages/list/list.ts b/src/addon/notifications/pages/list/list.ts
index e9634d095..9031abdc0 100644
--- a/src/addon/notifications/pages/list/list.ts
+++ b/src/addon/notifications/pages/list/list.ts
@@ -121,7 +121,7 @@ export class AddonNotificationsListPage {
// Check if mark all notifications as read is enabled and there are some to read.
if (this.notificationsProvider.isMarkAllNotificationsAsReadEnabled()) {
promises.push(this.notificationsProvider.getUnreadNotificationsCount().then((unread) => {
- this.canMarkAllNotificationsAsRead = unread > 0
+ this.canMarkAllNotificationsAsRead = unread > 0;
}));
} else {
this.canMarkAllNotificationsAsRead = false;
diff --git a/src/addon/notifications/providers/notifications.ts b/src/addon/notifications/providers/notifications.ts
index d7a1bde1b..bbd74caa4 100644
--- a/src/addon/notifications/providers/notifications.ts
+++ b/src/addon/notifications/providers/notifications.ts
@@ -244,10 +244,10 @@ export class AddonNotificationsProvider {
const params = {
useridto: this.sitesProvider.getCurrentSiteUserId()
};
+
return this.sitesProvider.getCurrentSite().write('core_message_mark_all_notifications_as_read', params);
}
-
/**
* Mark message notification as read.
*
diff --git a/src/components/split-view/split-view.ts b/src/components/split-view/split-view.ts
index ec7dbd4a4..8808fa88e 100644
--- a/src/components/split-view/split-view.ts
+++ b/src/components/split-view/split-view.ts
@@ -109,6 +109,10 @@ export class CoreSplitViewComponent implements OnInit, OnDestroy {
handleCanLeave(): void {
// Listen for the didEnter event on the details nav to detect everytime a page is loaded.
this.detailsDidEnterSubscription = this.detailNav.viewDidEnter.subscribe((detailsViewController: ViewController) => {
+ if (!this.isOn()) {
+ return;
+ }
+
const masterViewController = this.masterNav.getActive();
if (this.masterCanLeaveOverridden) {
@@ -133,7 +137,7 @@ export class CoreSplitViewComponent implements OnInit, OnDestroy {
return Promise.resolve().then(() => {
if (this.originalMasterCanLeave) {
// First call the master canLeave.
- const result = this.originalMasterCanLeave();
+ const result = this.originalMasterCanLeave.apply(masterViewController.instance);
if (typeof result == 'boolean' && !result) {
// User cannot leave, return a rejected promise so the details canLeave isn't executed.
return Promise.reject(null);
diff --git a/src/core/course/providers/module-delegate.ts b/src/core/course/providers/module-delegate.ts
index b367efbf1..459110be2 100644
--- a/src/core/course/providers/module-delegate.ts
+++ b/src/core/course/providers/module-delegate.ts
@@ -14,6 +14,7 @@
import { Injectable, Injector } from '@angular/core';
import { NavController, NavOptions } from 'ionic-angular';
+import { SafeUrl } from '@angular/platform-browser';
import { CoreEventsProvider } from '@providers/events';
import { CoreLoggerProvider } from '@providers/logger';
import { CoreSitesProvider } from '@providers/sites';
@@ -77,7 +78,7 @@ export interface CoreCourseModuleHandlerData {
* The image to use as icon (path to the image).
* @type {string}
*/
- icon?: string;
+ icon?: string | SafeUrl;
/**
* The class to assign to the item.
diff --git a/src/core/siteplugins/providers/siteplugins.ts b/src/core/siteplugins/providers/siteplugins.ts
index 2dab2bbf2..d8d5a4ab1 100644
--- a/src/core/siteplugins/providers/siteplugins.ts
+++ b/src/core/siteplugins/providers/siteplugins.ts
@@ -149,15 +149,19 @@ export class CoreSitePluginsProvider {
* @return {any} An object with the data to pass to the JS.
*/
createDataForJS(initResult: any, contentResult?: any): any {
- // First of all, add the data returned by the init JS (if any).
- let data = this.utils.clone(initResult.jsResult || {});
- if (typeof data == 'boolean') {
- data = {};
- }
+ let data;
- // Now add some data returned by the init WS call.
- data.INIT_TEMPLATES = this.utils.objectToKeyValueMap(initResult.templates, 'id', 'html');
- data.INIT_OTHERDATA = initResult.otherdata;
+ if (initResult) {
+ // First of all, add the data returned by the init JS (if any).
+ data = this.utils.clone(initResult.jsResult || {});
+ if (typeof data == 'boolean') {
+ data = {};
+ }
+
+ // Now add some data returned by the init WS call.
+ data.INIT_TEMPLATES = this.utils.objectToKeyValueMap(initResult.templates, 'id', 'html');
+ data.INIT_OTHERDATA = initResult.otherdata;
+ }
if (contentResult) {
// Now add the data returned by the content WS call.
diff --git a/src/providers/local-notifications.ts b/src/providers/local-notifications.ts
index 4002a066d..29ea131a2 100644
--- a/src/providers/local-notifications.ts
+++ b/src/providers/local-notifications.ts
@@ -125,17 +125,19 @@ export class CoreLocalNotificationsProvider {
this.appDB = appProvider.getDB();
this.appDB.createTablesFromSchema(this.tablesSchema);
- localNotifications.on('trigger', (notification, state) => {
- this.trigger(notification);
- });
+ platform.ready().then(() => {
+ localNotifications.on('trigger', (notification, state) => {
+ this.trigger(notification);
+ });
- localNotifications.on('click', (notification, state) => {
- if (notification && notification.data) {
- this.logger.debug('Notification clicked: ', notification.data);
+ localNotifications.on('click', (notification, state) => {
+ if (notification && notification.data) {
+ this.logger.debug('Notification clicked: ', notification.data);
- const data = textUtils.parseJSON(notification.data);
- this.notifyClick(data);
- }
+ const data = textUtils.parseJSON(notification.data);
+ this.notifyClick(data);
+ }
+ });
});
eventsProvider.on(CoreEventsProvider.SITE_DELETED, (site) => {
diff --git a/src/theme/variables.scss b/src/theme/variables.scss
index 908caf8ec..10e70f4b2 100644
--- a/src/theme/variables.scss
+++ b/src/theme/variables.scss
@@ -149,6 +149,9 @@ $tabs-ios-tab-color-inactive: $tabs-tab-color-inactive;
$button-ios-outline-background-color: $core-button-outline-background-color;
$toolbar-ios-height: 44px + 8; // Avoid toolbar with different heights.
$checkbox-ios-icon-border-radius: 0px !default;
+$radio-ios-disabled-opacity: .5 !default;
+$checkbox-ios-disabled-opacity: .5 !default;
+$toggle-ios-disabled-opacity: .5 !default;
// App Material Design Variables
// --------------------------------------------------
@@ -163,6 +166,9 @@ $spinner-md-crescent-color: $core-spinner-color;
$tabs-md-tab-color-inactive: $tabs-tab-color-inactive;
$button-md-outline-background-color: $core-button-outline-background-color;
$font-family-md-base: "Roboto", "Noto Sans", "Helvetica Neue", sans-serif !default;
+$radio-md-disabled-opacity: .5 !default;
+$checkbox-md-disabled-opacity: .5 !default;
+$toggle-md-disabled-opacity: .5 !default;
// App Windows Variables
// --------------------------------------------------
@@ -175,7 +181,9 @@ $loading-wp-spinner-color: $core-loading-spinner-color;
$spinner-wp-circles-color: $core-spinner-color;
$tabs-wp-tab-color-inactive: $tabs-tab-color-inactive;
$button-wp-outline-background-color: $core-button-outline-background-color;
-
+$radio-wp-disabled-opacity: .5 !default;
+$checkbox-wp-disabled-opacity: .5 !default;
+$toggle-wp-disabled-opacity: .5 !default;
// App Theme
// --------------------------------------------------