commit
78a92e5509
|
@ -33,7 +33,7 @@
|
|||
<preference name="AndroidPersistentFileLocation" value="Compatibility" />
|
||||
<preference name="iosPersistentFileLocation" value="Compatibility" />
|
||||
<preference name="SplashScreen" value="screen" />
|
||||
<preference name="SplashScreenDelay" value="10000" />
|
||||
<preference name="SplashScreenDelay" value="15000" />
|
||||
<preference name="SplashMaintainAspectRatio" value="true" />
|
||||
<feature name="StatusBar">
|
||||
<param name="ios-package" onload="true" value="CDVStatusBar" />
|
||||
|
|
|
@ -73,5 +73,8 @@ export class AddonCalendarModule {
|
|||
'categoryid', 'groupid', 'userid', 'instance', 'modulename', 'timemodified', 'repeatid', 'visible', 'uuid',
|
||||
'sequence', 'subscriptionid', 'notificationtime']
|
||||
});
|
||||
|
||||
// Migrate the component name.
|
||||
updateManager.registerLocalNotifComponentMigration('mmaCalendarComponent', AddonCalendarProvider.COMPONENT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,9 +31,9 @@ export class AddonCalendarProvider {
|
|||
static DAYS_INTERVAL = 30;
|
||||
static COMPONENT = 'AddonCalendarEvents';
|
||||
static DEFAULT_NOTIFICATION_TIME_CHANGED = 'AddonCalendarDefaultNotificationTimeChangedEvent';
|
||||
protected DEFAULT_NOTIFICATION_TIME_SETTING = 'mmaCalendarDefaultNotifTime';
|
||||
static DEFAULT_NOTIFICATION_TIME_SETTING = 'mmaCalendarDefaultNotifTime';
|
||||
static DEFAULT_NOTIFICATION_TIME = 60;
|
||||
protected ROOT_CACHE_KEY = 'mmaCalendar:';
|
||||
protected DEFAULT_NOTIFICATION_TIME = 60;
|
||||
|
||||
// Variables for database.
|
||||
static EVENTS_TABLE = 'addon_calendar_events';
|
||||
|
@ -136,6 +136,18 @@ export class AddonCalendarProvider {
|
|||
this.sitesProvider.createTablesFromSchema(this.tablesSchema);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all calendar events from local Db.
|
||||
*
|
||||
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
|
||||
* @return {Promise<any[]>} Promise resolved with all the events.
|
||||
*/
|
||||
getAllEventsFromLocalDb(siteId?: string): Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().getAllRecords(AddonCalendarProvider.EVENTS_TABLE);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured default notification time.
|
||||
*
|
||||
|
@ -145,9 +157,9 @@ export class AddonCalendarProvider {
|
|||
getDefaultNotificationTime(siteId?: string): Promise<number> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
const key = this.DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId;
|
||||
const key = AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId;
|
||||
|
||||
return this.configProvider.get(key, this.DEFAULT_NOTIFICATION_TIME);
|
||||
return this.configProvider.get(key, AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -496,11 +508,24 @@ export class AddonCalendarProvider {
|
|||
setDefaultNotificationTime(time: number, siteId?: string): Promise<any[]> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
const key = this.DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId;
|
||||
const key = AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId;
|
||||
|
||||
return this.configProvider.set(key, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an event in local DB as it is.
|
||||
*
|
||||
* @param {any} event Event to store.
|
||||
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
|
||||
* @return {Promise<any>} Promise resolved when stored.
|
||||
*/
|
||||
storeEventInLocalDb(event: any, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().insertRecord(AddonCalendarProvider.EVENTS_TABLE, event);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Store events in local DB.
|
||||
*
|
||||
|
|
|
@ -131,5 +131,9 @@ export class AddonMessagesModule {
|
|||
}
|
||||
]
|
||||
});
|
||||
|
||||
// Migrate the component name.
|
||||
updateManager.registerLocalNotifComponentMigration('mmaMessagesPushSimulation',
|
||||
AddonMessagesProvider.PUSH_SIMULATION_COMPONENT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,7 @@
|
|||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-label stacked>{{ 'addon.mod_glossary.definition' | translate }}</ion-label>
|
||||
<core-rich-text-editor item-content [control]="definitionControl" (contentChanged)="onDefinitionChange($event)" [placeholder]="'addon.mod_glossary.definition' | translate" name="addon_mod_glossary_edit"></core-rich-text-editor>
|
||||
<!-- @todo: Attributes that were passed to RTE in Ionic 1 but now they aren't supported yet:
|
||||
[component]="component" [componentId]="glossary.cmid" -->
|
||||
<core-rich-text-editor item-content [control]="definitionControl" (contentChanged)="onDefinitionChange($event)" [placeholder]="'addon.mod_glossary.definition' | translate" name="addon_mod_glossary_edit" [component]="component" [componentId]="glossary.cmid"></core-rich-text-editor>
|
||||
</ion-item>
|
||||
<ion-item *ngIf="categories.length > 0">
|
||||
<ion-label stacked id="addon-mod-glossary-categories-label">{{ 'addon.mod_glossary.categories' | translate }}</ion-label>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
<ion-item>
|
||||
<core-show-password item-content [name]="'password'">
|
||||
<ion-label>{{ 'addon.mod_lesson.enterpassword' | translate }}</ion-label>
|
||||
<ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" core-auto-focus #passwordinput></ion-input>
|
||||
<ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" [core-auto-focus] #passwordinput></ion-input>
|
||||
</core-show-password>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<ion-item>
|
||||
<core-show-password item-content [name]="'password'">
|
||||
<ion-label>{{ 'addon.mod_lesson.enterpassword' | translate }}</ion-label>
|
||||
<ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" core-auto-focus #passwordinput></ion-input>
|
||||
<ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" [core-auto-focus] #passwordinput></ion-input>
|
||||
</core-show-password>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
<core-rich-text-editor item-content [control]="contentControl" [placeholder]="'core.content' | translate" name="wiki_page_content" [component]="component" [componentId]="componentId"></core-rich-text-editor>
|
||||
</ion-item>
|
||||
|
||||
<ion-badge color="danger" *ngIf="wrongVersionLock" item-end>{{ 'addon.mod_wiki.wrongversionlock' | translate }}</ion-badge> <!-- @todo: Check this. -->
|
||||
<ion-item *ngIf="wrongVersionLock" text-center class="addon-mod_wiki-wrongversionlock" >
|
||||
<ion-badge color="danger" padding>{{ 'addon.mod_wiki.wrongversionlock' | translate }}</ion-badge>
|
||||
</ion-item>
|
||||
</form>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
page-addon-mod-wiki-edit {
|
||||
.addon-mod_wiki-wrongversionlock .label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
|
@ -83,5 +83,8 @@ export class AddonPushNotificationsModule {
|
|||
}
|
||||
]
|
||||
});
|
||||
|
||||
// Migrate the component name.
|
||||
updateManager.registerLocalNotifComponentMigration('mmaPushNotifications', AddonPushNotificationsProvider.COMPONENT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@
|
|||
</ion-item>
|
||||
<!-- Edit. -->
|
||||
<ion-item *ngIf="edit && field && field.shortname" [formGroup]="form">
|
||||
<ion-label [core-mark-required]="field.required">{{ field.name }}</ion-label>
|
||||
<ion-label>
|
||||
<span [core-mark-required]="field.required">{{ field.name }}</span>
|
||||
<core-input-errors [control]="form.controls[field.modelName]"></core-input-errors>
|
||||
</ion-label>
|
||||
<ion-checkbox item-end [formControlName]="field.modelName">
|
||||
</ion-checkbox>
|
||||
<core-input-errors [control]="form.controls[field.modelName]" [errorMessages]="errors"></core-input-errors>
|
||||
</ion-item>
|
|
@ -7,4 +7,5 @@
|
|||
<ion-item *ngIf="edit && field && field.shortname" text-wrap [formGroup]="form">
|
||||
<ion-label stacked [core-mark-required]="field.required">{{ field.name }}</ion-label>
|
||||
<ion-datetime [formControlName]="field.modelName" [placeholder]="'core.choosedots' | translate" [displayFormat]="field.format" [max]="field.max" [min]="field.min"></ion-datetime>
|
||||
<core-input-errors item-content [control]="form.controls[field.modelName]"></core-input-errors>
|
||||
</ion-item>
|
|
@ -10,4 +10,5 @@
|
|||
<ion-option value="">{{ 'core.choosedots' | translate }}</ion-option>
|
||||
<ion-option *ngFor="let option of field.options" [value]="option">{{option}}</ion-option>
|
||||
</ion-select>
|
||||
<core-input-errors item-content [control]="form.controls[field.modelName]"></core-input-errors>
|
||||
</ion-item>
|
||||
|
|
|
@ -7,4 +7,5 @@
|
|||
<ion-item *ngIf="edit && field && field.shortname" text-wrap [formGroup]="form">
|
||||
<ion-label stacked [core-mark-required]="field.required">{{ field.name }}</ion-label>
|
||||
<ion-input [type]="field.inputType" [formControlName]="field.modelName" [placeholder]="field.name" maxlength="{{field.maxlength}}"></ion-input>
|
||||
<core-input-errors item-content [control]="form.controls[field.modelName]"></core-input-errors>
|
||||
</ion-item>
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
</ion-item>
|
||||
<!-- Edit. -->
|
||||
<ion-item *ngIf="edit && field && field.shortname" text-wrap [formGroup]="form">
|
||||
<ion-label stacked [core-mark-required]="field.required">{{ field.name }}</ion-label>
|
||||
<ion-label stacked>
|
||||
<span [core-mark-required]="field.required">{{ field.name }}</span>
|
||||
<core-input-errors [control]="control"></core-input-errors>
|
||||
</ion-label>
|
||||
<core-rich-text-editor item-content [control]="control" [placeholder]="field.name"></core-rich-text-editor>
|
||||
</ion-item>
|
|
@ -15,7 +15,6 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Platform } from 'ionic-angular';
|
||||
import { StatusBar } from '@ionic-native/status-bar';
|
||||
import { SplashScreen } from '@ionic-native/splash-screen';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreLangProvider } from '@providers/lang';
|
||||
|
@ -33,7 +32,7 @@ export class MoodleMobileApp implements OnInit {
|
|||
protected logger;
|
||||
protected lastUrls = {};
|
||||
|
||||
constructor(private platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, logger: CoreLoggerProvider,
|
||||
constructor(private platform: Platform, statusBar: StatusBar, logger: CoreLoggerProvider,
|
||||
private eventsProvider: CoreEventsProvider, private loginHelper: CoreLoginHelperProvider,
|
||||
private appProvider: CoreAppProvider, private langProvider: CoreLangProvider, private sitesProvider: CoreSitesProvider) {
|
||||
this.logger = logger.getInstance('AppComponent');
|
||||
|
@ -46,8 +45,6 @@ export class MoodleMobileApp implements OnInit {
|
|||
} else {
|
||||
statusBar.styleDefault();
|
||||
}
|
||||
|
||||
splashScreen.hide();
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -1226,13 +1226,14 @@ export class CoreSite {
|
|||
}
|
||||
|
||||
if (alertMessage) {
|
||||
const alert = this.domUtils.showAlert(this.translate.instant('core.notice'), alertMessage, undefined, 3000);
|
||||
alert.onDidDismiss(() => {
|
||||
if (inApp) {
|
||||
resolve(this.utils.openInApp(url, options));
|
||||
} else {
|
||||
resolve(this.utils.openInBrowser(url));
|
||||
}
|
||||
this.domUtils.showAlert(this.translate.instant('core.notice'), alertMessage, undefined, 3000).then((alert) => {
|
||||
alert.onDidDismiss(() => {
|
||||
if (inApp) {
|
||||
resolve(this.utils.openInApp(url, options));
|
||||
} else {
|
||||
resolve(this.utils.openInBrowser(url));
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if (inApp) {
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
<div class="core-input-error-container" role="alert" *ngIf="(formControl && formControl.dirty && !formControl.valid) || errorText">
|
||||
<ng-container *ngIf="formControl && formControl.dirty && !formControl.valid">
|
||||
<ng-container *ngFor="let error of errorKeys">
|
||||
<div *ngIf="formControl.hasError(error)" class="core-input-error">{{errorMessages[error]}}</div>
|
||||
<div *ngIf="formControl.hasError(error)" class="core-input-error">
|
||||
<span *ngIf="errorMessages[error]">{{errorMessages[error]}}</span>
|
||||
<span *ngIf="!errorMessages[error] && error == 'max' && formControl.errors.max">
|
||||
{{ 'core.login.invalidvaluemax' | translate:{$a: formControl.errors.max.max} }}
|
||||
</span>
|
||||
<span *ngIf="!errorMessages[error] && error == 'min' && formControl.errors.min">
|
||||
{{ 'core.login.invalidvaluemin' | translate:{$a: formControl.errors.min.min} }}
|
||||
</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<div *ngIf="errorText" class="core-input-error">{{ errorText }}</div>
|
||||
|
|
|
@ -75,7 +75,9 @@ export class CoreInputErrorsComponent implements OnInit, OnChanges {
|
|||
this.errorMessages.time = this.errorMessages.time || this.translate.instant('core.login.invalidtime');
|
||||
this.errorMessages.url = this.errorMessages.url || this.translate.instant('core.login.invalidurl');
|
||||
|
||||
// @todo: Check how to handle min/max errors once we have a test case to use. Also, review previous errors.
|
||||
// Set empty values by default, the default error messages will be built in the template when needed.
|
||||
this.errorMessages.max = this.errorMessages.max || '';
|
||||
this.errorMessages.min = this.errorMessages.min || '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -197,6 +197,7 @@ export class CoreRichTextEditorComponent implements AfterContentInit, OnDestroy
|
|||
} else {
|
||||
// Don't emit event so our valueChanges doesn't get notified by this change.
|
||||
this.control.setValue(this.editorElement.innerHTML, {emitEvent: false});
|
||||
this.control.markAsDirty();
|
||||
this.textarea.value = this.editorElement.innerHTML;
|
||||
}
|
||||
} else {
|
||||
|
@ -205,6 +206,7 @@ export class CoreRichTextEditorComponent implements AfterContentInit, OnDestroy
|
|||
} else {
|
||||
// Don't emit event so our valueChanges doesn't get notified by this change.
|
||||
this.control.setValue(this.textarea.value, {emitEvent: false});
|
||||
this.control.markAsDirty();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,6 +378,11 @@ export class CoreRichTextEditorComponent implements AfterContentInit, OnDestroy
|
|||
* Treating videos and audios in here is complex, so if a user manually adds one he won't be able to play it in the editor.
|
||||
*/
|
||||
protected treatExternalContent(): void {
|
||||
if (!this.sitesProvider.isLoggedIn()) {
|
||||
// Only treat external content if the user is logged in.
|
||||
return;
|
||||
}
|
||||
|
||||
const elements = Array.from(this.editorElement.querySelectorAll('img')),
|
||||
siteId = this.sitesProvider.getCurrentSiteId(),
|
||||
canDownloadFiles = this.sitesProvider.getCurrentSite().canDownloadFiles();
|
||||
|
|
|
@ -41,7 +41,6 @@ import { NavController, Nav } from 'ionic-angular';
|
|||
templateUrl: 'core-split-view.html'
|
||||
})
|
||||
export class CoreSplitViewComponent implements OnInit {
|
||||
// @todo Mix both panels header buttons
|
||||
|
||||
@ViewChild('detailNav') detailNav: Nav;
|
||||
@Input() when?: string | boolean = 'md';
|
||||
|
|
|
@ -83,7 +83,7 @@ import { CoreSitePluginsQuizAccessRuleComponent } from '@core/siteplugins/compon
|
|||
import { CoreSitePluginsAssignFeedbackComponent } from '@core/siteplugins/components/assign-feedback/assign-feedback';
|
||||
import { CoreSitePluginsAssignSubmissionComponent } from '@core/siteplugins/components/assign-submission/assign-submission';
|
||||
|
||||
// Import addon providers. Do not import database module because it causes circular dependencies. @todo workshop
|
||||
// Import addon providers. Do not import database module because it causes circular dependencies.
|
||||
import { ADDON_BADGES_PROVIDERS } from '@addon/badges/badges.module';
|
||||
import { ADDON_CALENDAR_PROVIDERS } from '@addon/calendar/calendar.module';
|
||||
import { ADDON_COMPETENCY_PROVIDERS } from '@addon/competency/competency.module';
|
||||
|
|
|
@ -819,8 +819,6 @@ export class CoreCourseHelperProvider {
|
|||
moduleInfo.sizeReadable = this.textUtils.bytesToSize(moduleSize, 2);
|
||||
}));
|
||||
|
||||
// @todo: Decide what to display instead of timemodified. Last check_updates?
|
||||
|
||||
promises.push(this.prefetchDelegate.getModuleStatus(module, courseId).then((moduleStatus) => {
|
||||
moduleInfo.status = moduleStatus;
|
||||
switch (moduleStatus) {
|
||||
|
|
|
@ -128,9 +128,11 @@
|
|||
<p><a [href]="settings.sitepolicy" core-link capture="false">{{ 'core.login.policyagreementclick' | translate }}</a></p>
|
||||
</ion-item>
|
||||
<ion-item text-wrap>
|
||||
<ion-label core-mark-required="true">{{ 'core.login.policyaccept' | translate }}</ion-label>
|
||||
<ion-label>
|
||||
<span [core-mark-required]="true">{{ 'core.login.policyaccept' | translate }}</span>
|
||||
<core-input-errors [control]="signupForm.controls.policyagreed" [errorMessages]="policyErrors"></core-input-errors>
|
||||
</ion-label>
|
||||
<ion-checkbox item-end name="policyagreed" formControlName="policyagreed"></ion-checkbox>
|
||||
<core-input-errors [control]="signupForm.controls.policyagreed" [errorMessages]="policyErrors"></core-input-errors>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
</ion-item>
|
||||
</div>
|
||||
<ion-item>
|
||||
<ion-input type="text" name="value" placeholder="{{ 'core.login.usernameoremail' | translate }}" formControlName="value" autocapitalize="none" autocorrect="off" core-auto-focus></ion-input>
|
||||
<ion-input type="text" name="value" placeholder="{{ 'core.login.usernameoremail' | translate }}" formControlName="value" autocapitalize="none" autocorrect="off" [core-auto-focus]></ion-input>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<button text-wrap ion-button block [disabled]="!myForm.valid">{{ 'core.courses.search' | translate }}</button>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import { Component } from '@angular/core';
|
||||
import { IonicPage, NavController } from 'ionic-angular';
|
||||
import { SplashScreen } from '@ionic-native/splash-screen';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreInitDelegate } from '@providers/init';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
|
@ -31,7 +32,8 @@ import { CoreLoginHelperProvider } from '../../providers/helper';
|
|||
export class CoreLoginInitPage {
|
||||
|
||||
constructor(private navCtrl: NavController, private appProvider: CoreAppProvider, private initDelegate: CoreInitDelegate,
|
||||
private sitesProvider: CoreSitesProvider, private loginHelper: CoreLoginHelperProvider) { }
|
||||
private sitesProvider: CoreSitesProvider, private loginHelper: CoreLoginHelperProvider,
|
||||
private splashScreen: SplashScreen) { }
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
|
@ -51,11 +53,11 @@ export class CoreLoginInitPage {
|
|||
// The redirect is pointing to a site, load it.
|
||||
return this.sitesProvider.loadSite(redirectData.siteId).then(() => {
|
||||
if (!this.loginHelper.isSiteLoggedOut(redirectData.page, redirectData.params)) {
|
||||
this.navCtrl.setRoot(redirectData.page, redirectData.params, { animate: false });
|
||||
return this.navCtrl.setRoot(redirectData.page, redirectData.params, { animate: false });
|
||||
}
|
||||
}).catch(() => {
|
||||
// Site doesn't exist.
|
||||
this.loadPage();
|
||||
return this.loadPage();
|
||||
});
|
||||
} else {
|
||||
// No site to load, just open the state.
|
||||
|
@ -64,24 +66,37 @@ export class CoreLoginInitPage {
|
|||
}
|
||||
}
|
||||
|
||||
this.loadPage();
|
||||
return this.loadPage();
|
||||
}).then(() => {
|
||||
// If we hide the splash screen now, the init view is still seen for an instant. Wait a bit to make sure it isn't seen.
|
||||
setTimeout(() => {
|
||||
this.splashScreen.hide();
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the right page.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected loadPage(): void {
|
||||
protected loadPage(): Promise<any> {
|
||||
if (this.sitesProvider.isLoggedIn()) {
|
||||
if (!this.loginHelper.isSiteLoggedOut()) {
|
||||
this.loginHelper.goToSiteInitialPage();
|
||||
// User is logged in, go to site initial page.
|
||||
return this.loginHelper.goToSiteInitialPage();
|
||||
} else {
|
||||
// The site is marked as logged out. Logout and try again.
|
||||
return this.sitesProvider.logout().then(() => {
|
||||
return this.loadPage();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.sitesProvider.hasSites().then((hasSites) => {
|
||||
return this.sitesProvider.hasSites().then((hasSites) => {
|
||||
if (hasSites) {
|
||||
this.navCtrl.setRoot('CoreLoginSitesPage');
|
||||
return this.navCtrl.setRoot('CoreLoginSitesPage');
|
||||
} else {
|
||||
this.loginHelper.goToAddSite(true);
|
||||
return this.loginHelper.goToAddSite(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<form [formGroup]="credForm" (ngSubmit)="login()">
|
||||
<ion-item>
|
||||
<core-show-password item-content [name]="'password'">
|
||||
<ion-input class="core-ioninput-password" name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" formControlName="password" core-auto-focus></ion-input>
|
||||
<ion-input class="core-ioninput-password" name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" formControlName="password" [core-auto-focus]></ion-input>
|
||||
</core-show-password>
|
||||
</ion-item>
|
||||
<ion-grid>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<div *ngIf="!fixedSites">
|
||||
<p padding>{{ 'core.login.newsitedescription' | translate }}</p>
|
||||
<ion-item>
|
||||
<ion-input type="url" name="url" placeholder="{{ 'core.login.siteaddress' | translate }}" formControlName="siteUrl" [core-auto-focus]></ion-input>
|
||||
<ion-input type="url" name="url" placeholder="{{ 'core.login.siteaddress' | translate }}" formControlName="siteUrl" [core-auto-focus]="showKeyboard"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { IonicPage, NavController, ModalController } from 'ionic-angular';
|
||||
import { IonicPage, NavController, ModalController, NavParams } from 'ionic-angular';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
|
@ -33,10 +33,14 @@ export class CoreLoginSitePage {
|
|||
siteForm: FormGroup;
|
||||
fixedSites: any[];
|
||||
displayAsButtons = false;
|
||||
showKeyboard = false;
|
||||
|
||||
constructor(private navCtrl: NavController, fb: FormBuilder, private appProvider: CoreAppProvider,
|
||||
constructor(navParams: NavParams, private navCtrl: NavController, fb: FormBuilder, private appProvider: CoreAppProvider,
|
||||
private sitesProvider: CoreSitesProvider, private loginHelper: CoreLoginHelperProvider,
|
||||
private modalCtrl: ModalController, private domUtils: CoreDomUtilsProvider) {
|
||||
|
||||
this.showKeyboard = !!navParams.get('showKeyboard');
|
||||
|
||||
let url = '';
|
||||
|
||||
// Load fixed sites if they're set.
|
||||
|
|
|
@ -67,7 +67,7 @@ export class CoreLoginSitesPage {
|
|||
* Go to the page to add a site.
|
||||
*/
|
||||
add(): void {
|
||||
this.loginHelper.goToAddSite(false);
|
||||
this.loginHelper.goToAddSite(false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,7 +91,7 @@ export class CoreLoginSitesPage {
|
|||
// If there are no sites left, go to add site.
|
||||
this.sitesProvider.hasSites().then((hasSites) => {
|
||||
if (!hasSites) {
|
||||
this.loginHelper.goToAddSite(true);
|
||||
this.loginHelper.goToAddSite(true, true);
|
||||
}
|
||||
});
|
||||
}).catch((error) => {
|
||||
|
|
|
@ -380,9 +380,10 @@ export class CoreLoginHelperProvider {
|
|||
* If a fixed URL is configured, go to credentials instead.
|
||||
*
|
||||
* @param {boolean} [setRoot] True to set the new page as root, false to add it to the stack.
|
||||
* @param {boolean} [showKeyboard] Whether to show keyboard in the new page. Only if no fixed URL set.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
goToAddSite(setRoot?: boolean): Promise<any> {
|
||||
goToAddSite(setRoot?: boolean, showKeyboard?: boolean): Promise<any> {
|
||||
let pageName,
|
||||
params;
|
||||
|
||||
|
@ -395,6 +396,9 @@ export class CoreLoginHelperProvider {
|
|||
params = { siteUrl: url };
|
||||
} else {
|
||||
pageName = 'CoreLoginSitePage';
|
||||
params = {
|
||||
showKeyboard: showKeyboard
|
||||
};
|
||||
}
|
||||
|
||||
if (setRoot) {
|
||||
|
@ -685,9 +689,10 @@ export class CoreLoginHelperProvider {
|
|||
* @param {string} error Error message.
|
||||
*/
|
||||
openChangePassword(siteUrl: string, error: string): void {
|
||||
const alert = this.domUtils.showAlert(this.translate.instant('core.notice'), error, undefined, 3000);
|
||||
alert.onDidDismiss(() => {
|
||||
this.utils.openInApp(siteUrl + '/login/change_password.php');
|
||||
this.domUtils.showAlert(this.translate.instant('core.notice'), error, undefined, 3000).then((alert) => {
|
||||
alert.onDidDismiss(() => {
|
||||
this.utils.openInApp(siteUrl + '/login/change_password.php');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
<core-site-plugins-plugin-content *ngIf="component && method" [component]="component" [method]="method" [args]="args" [initResult]="initResult"></core-site-plugins-plugin-content>
|
||||
<core-site-plugins-plugin-content *ngIf="component && method" [component]="component" [method]="method" [args]="args" [initResult]="initResult" [data]="data"></core-site-plugins-plugin-content>
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, Input, ViewChild } from '@angular/core';
|
||||
import { Component, OnChanges, Input, ViewChild } from '@angular/core';
|
||||
import { CoreSitePluginsProvider } from '../../providers/siteplugins';
|
||||
import { CoreSitePluginsPluginContentComponent } from '../plugin-content/plugin-content';
|
||||
|
||||
|
@ -23,10 +23,12 @@ import { CoreSitePluginsPluginContentComponent } from '../plugin-content/plugin-
|
|||
selector: 'core-site-plugins-course-format',
|
||||
templateUrl: 'core-siteplugins-course-format.html',
|
||||
})
|
||||
export class CoreSitePluginsCourseFormatComponent implements OnInit {
|
||||
export class CoreSitePluginsCourseFormatComponent implements OnChanges {
|
||||
@Input() course: any; // The course to render.
|
||||
@Input() sections: any[]; // List of course sections.
|
||||
@Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled.
|
||||
@Input() initialSectionId?: number; // The section to load first (by ID).
|
||||
@Input() initialSectionNumber?: number; // The section to load first (by number).
|
||||
|
||||
@ViewChild(CoreSitePluginsPluginContentComponent) content: CoreSitePluginsPluginContentComponent;
|
||||
|
||||
|
@ -34,24 +36,37 @@ export class CoreSitePluginsCourseFormatComponent implements OnInit {
|
|||
method: string;
|
||||
args: any;
|
||||
initResult: any;
|
||||
data: any;
|
||||
|
||||
constructor(protected sitePluginsProvider: CoreSitePluginsProvider) { }
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
* Detect changes on input properties.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
ngOnChanges(): void {
|
||||
if (this.course && this.course.format) {
|
||||
const handler = this.sitePluginsProvider.getSitePluginHandler(this.course.format);
|
||||
if (handler) {
|
||||
this.component = handler.plugin.component;
|
||||
this.method = handler.handlerSchema.method;
|
||||
this.args = {
|
||||
courseid: this.course.id,
|
||||
downloadenabled: this.downloadEnabled
|
||||
};
|
||||
this.initResult = handler.initResult;
|
||||
if (!this.component) {
|
||||
// Initialize the data.
|
||||
const handler = this.sitePluginsProvider.getSitePluginHandler(this.course.format);
|
||||
if (handler) {
|
||||
this.component = handler.plugin.component;
|
||||
this.method = handler.handlerSchema.method;
|
||||
this.args = {
|
||||
courseid: this.course.id,
|
||||
downloadenabled: this.downloadEnabled
|
||||
};
|
||||
this.initResult = handler.initResult;
|
||||
}
|
||||
}
|
||||
|
||||
// Pass input data to the component.
|
||||
this.data = {
|
||||
course: this.course,
|
||||
sections: this.sections,
|
||||
downloadEnabled: this.downloadEnabled,
|
||||
initialSectionId: this.initialSectionId,
|
||||
initialSectionNumber: this.initialSectionNumber
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, Input, Output, EventEmitter, Optional } from '@angular/core';
|
||||
import { Component, OnInit, Input, Output, EventEmitter, Optional, DoCheck, KeyValueDiffers } from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreSitePluginsProvider } from '../../providers/siteplugins';
|
||||
|
@ -25,11 +25,12 @@ import { Subject } from 'rxjs';
|
|||
selector: 'core-site-plugins-plugin-content',
|
||||
templateUrl: 'core-siteplugins-plugin-content.html',
|
||||
})
|
||||
export class CoreSitePluginsPluginContentComponent implements OnInit {
|
||||
export class CoreSitePluginsPluginContentComponent implements OnInit, DoCheck {
|
||||
@Input() component: string;
|
||||
@Input() method: string;
|
||||
@Input() args: any;
|
||||
@Input() initResult: any; // Result of the init WS call of the handler.
|
||||
@Input() data: any; // Data to pass to the component.
|
||||
@Output() onContentLoaded?: EventEmitter<boolean>; // Emits an event when the content is loaded.
|
||||
@Output() onLoadingContent?: EventEmitter<boolean>; // Emits an event when starts to load the content.
|
||||
|
||||
|
@ -40,11 +41,14 @@ export class CoreSitePluginsPluginContentComponent implements OnInit {
|
|||
invalidateObservable: Subject<void>; // An observable to notify observers when to invalidate data.
|
||||
jsData: any; // Data to pass to the component.
|
||||
|
||||
protected differ: any; // To detect changes in the data input.
|
||||
|
||||
constructor(protected domUtils: CoreDomUtilsProvider, protected sitePluginsProvider: CoreSitePluginsProvider,
|
||||
@Optional() protected navCtrl: NavController) {
|
||||
@Optional() protected navCtrl: NavController, differs: KeyValueDiffers) {
|
||||
this.onContentLoaded = new EventEmitter();
|
||||
this.onLoadingContent = new EventEmitter();
|
||||
this.invalidateObservable = new Subject<void>();
|
||||
this.differ = differs.find([]).create();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,6 +58,21 @@ export class CoreSitePluginsPluginContentComponent implements OnInit {
|
|||
this.fetchContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays).
|
||||
*/
|
||||
ngDoCheck(): void {
|
||||
if (!this.data || !this.jsData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if there's any change in the data object.
|
||||
const changes = this.differ.diff(this.data);
|
||||
if (changes) {
|
||||
this.jsData = Object.assign(this.jsData, this.data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the content to render.
|
||||
*
|
||||
|
@ -67,7 +86,9 @@ export class CoreSitePluginsPluginContentComponent implements OnInit {
|
|||
this.content = result.templates.length ? result.templates[0].html : ''; // Load first template.
|
||||
this.javascript = result.javascript;
|
||||
this.otherData = result.otherdata;
|
||||
this.jsData = this.sitePluginsProvider.createDataForJS(this.initResult, result);
|
||||
this.data = this.data || {};
|
||||
|
||||
this.jsData = Object.assign(this.data, this.sitePluginsProvider.createDataForJS(this.initResult, result));
|
||||
|
||||
// Pass some methods as jsData so they can be called from the template too.
|
||||
this.jsData.openContent = this.openContent.bind(this);
|
||||
|
|
|
@ -187,7 +187,6 @@ export class CoreExternalContentDirective implements AfterViewInit {
|
|||
this.logger.debug('Using URL ' + finalUrl + ' for ' + url);
|
||||
if (tagName === 'SOURCE') {
|
||||
// The browser does not catch changes in SRC, we need to add a new source.
|
||||
// @todo: Check if changing src works in Android 4.4, maybe the problem was only in 4.1-4.3.
|
||||
this.addSource(finalUrl);
|
||||
} else {
|
||||
this.element.setAttribute(targetAttr, finalUrl);
|
||||
|
|
|
@ -186,7 +186,6 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
this.element.innerHTML = ''; // Remove current contents.
|
||||
if (this.maxHeight && div.innerHTML != '') {
|
||||
// Move the children to the current element to be able to calculate the height.
|
||||
// @todo: Display the element?
|
||||
this.domUtils.moveChildren(div, this.element);
|
||||
|
||||
// Height cannot be calculated if the element is not shown while calculating.
|
||||
|
|
|
@ -90,8 +90,8 @@ export class CoreLinkDirective implements OnInit {
|
|||
href = href.substr(1);
|
||||
// In site links
|
||||
if (href.charAt(0) == '/') {
|
||||
// @todo: Investigate how to achieve this behaviour.
|
||||
// $location.url(href);
|
||||
// @todo: This cannot be achieved with push/pop navigation, location.go() doesn't update the state, only the URL.
|
||||
// In Ionic 4 the navigation will change, so maybe it can be done by then.
|
||||
} else {
|
||||
// Look for id or name.
|
||||
this.domUtils.scrollToElementBySelector(this.content, '#' + href + ', [name=\'' + href + '\']');
|
||||
|
|
|
@ -47,9 +47,6 @@ export interface CoreILocalNotification extends ILocalNotification {
|
|||
*
|
||||
* See https://angular.io/guide/dependency-injection for more info on providers
|
||||
* and Angular DI.
|
||||
*
|
||||
* @todo We might have to translate the old component name to the new one.
|
||||
* Otherwise the unique ID of local notifications could change.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreLocalNotificationsProvider {
|
||||
|
@ -502,4 +499,18 @@ export class CoreLocalNotificationsProvider {
|
|||
|
||||
return this.appDB.insertRecord(this.TRIGGERED_TABLE, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a component name.
|
||||
*
|
||||
* @param {string} oldName The old name.
|
||||
* @param {string} newName The new name.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
updateComponentName(oldName: string, newName: string): Promise<any> {
|
||||
const oldId = this.COMPONENTS_TABLE + '#' + oldName,
|
||||
newId = this.COMPONENTS_TABLE + '#' + newName;
|
||||
|
||||
return this.appDB.updateRecords(this.COMPONENTS_TABLE, {id: newId}, {id: oldId});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import { CoreLoggerProvider } from './logger';
|
|||
import { CoreSitesProvider } from './sites';
|
||||
import { CoreUtilsProvider } from './utils/utils';
|
||||
import { CoreConfigConstants } from '../configconstants';
|
||||
import { AddonCalendarProvider } from '@addon/calendar/providers/calendar';
|
||||
import { SQLiteDB } from '@classes/sqlitedb';
|
||||
|
||||
/**
|
||||
|
@ -71,6 +72,7 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
|
|||
|
||||
protected VERSION_APPLIED = 'version_applied';
|
||||
protected logger;
|
||||
protected localNotificationsComponentsMigrate: {[old: string]: string} = {};
|
||||
|
||||
/**
|
||||
* Tables to migrate from app DB ('MoodleMobile'). Include all the core ones to decrease the dependencies.
|
||||
|
@ -323,7 +325,8 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
|
|||
|
||||
constructor(logger: CoreLoggerProvider, private configProvider: CoreConfigProvider, private sitesProvider: CoreSitesProvider,
|
||||
private filepoolProvider: CoreFilepoolProvider, private notifProvider: CoreLocalNotificationsProvider,
|
||||
private utils: CoreUtilsProvider, private appProvider: CoreAppProvider) {
|
||||
private utils: CoreUtilsProvider, private appProvider: CoreAppProvider,
|
||||
private calendarProvider: AddonCalendarProvider) {
|
||||
this.logger = logger.getInstance('CoreUpdateManagerProvider');
|
||||
}
|
||||
|
||||
|
@ -341,6 +344,9 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
|
|||
if (!versionApplied) {
|
||||
// No version applied, either the app was just installed or it's being updated from Ionic 1.
|
||||
return this.migrateAllDBs().then(() => {
|
||||
// Now that the DBs have been migrated, migrate the local notification components names.
|
||||
return this.migrateLocalNotificationsComponents();
|
||||
}).then(() => {
|
||||
// DBs migrated, get the version applied again.
|
||||
return this.configProvider.get(this.VERSION_APPLIED, 0);
|
||||
});
|
||||
|
@ -358,9 +364,8 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
|
|||
promises.push(this.setSitesConfig());
|
||||
}
|
||||
|
||||
if (versionCode >= 2018 && versionApplied < 2018 && versionApplied > 0) {
|
||||
promises.push(this.adaptForumOfflineStores());
|
||||
}
|
||||
// In version 2018 we adapted the forum offline stores to match a new schema.
|
||||
// However, due to the migration of data to SQLite we can no longer do that.
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
return this.configProvider.set(this.VERSION_APPLIED, versionCode);
|
||||
|
@ -410,6 +415,16 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
|
|||
this.siteDBTables.push(table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a migration of component name for local notifications.
|
||||
*
|
||||
* @param {string} oldName The old name.
|
||||
* @param {string} newName The new name.
|
||||
*/
|
||||
registerLocalNotifComponentMigration(oldName: string, newName: string): void {
|
||||
this.localNotificationsComponentsMigrate[oldName] = newName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate all DBs and tables from the old format to SQLite.
|
||||
*
|
||||
|
@ -555,6 +570,30 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate local notifications components from the old nomenclature to the new one.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected migrateLocalNotificationsComponents(): Promise<any> {
|
||||
if (!this.notifProvider.isAvailable()) {
|
||||
// Local notifications not available, nothing to do.
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const promises = [];
|
||||
|
||||
for (const oldName in this.localNotificationsComponentsMigrate) {
|
||||
const newName = this.localNotificationsComponentsMigrate[oldName];
|
||||
|
||||
promises.push(this.notifProvider.updateComponentName(oldName, newName).catch((error) => {
|
||||
this.logger.error('Error migrating local notif component from ' + oldName + ' to ' + newName + ': ', error);
|
||||
}));
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calendar default notification time is configurable from version 3.2.1, and a new option "Default" is added.
|
||||
* All events that were configured to use the fixed default time should now be configured to use "Default" option.
|
||||
|
@ -567,8 +606,27 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// @todo: Implement it once Calendar addon is implemented.
|
||||
return Promise.resolve();
|
||||
return this.sitesProvider.getSitesIds().then((siteIds) => {
|
||||
|
||||
const promises = [];
|
||||
siteIds.forEach((siteId) => {
|
||||
// Get stored events.
|
||||
promises.push(this.calendarProvider.getAllEventsFromLocalDb(siteId).then((events) => {
|
||||
const eventPromises = [];
|
||||
|
||||
events.forEach((event) => {
|
||||
if (event.notificationtime == AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME) {
|
||||
event.notificationtime = -1;
|
||||
eventPromises.push(this.calendarProvider.storeEventInLocalDb(event, siteId));
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all(eventPromises);
|
||||
}));
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -626,15 +684,4 @@ export class CoreUpdateManagerProvider implements CoreInitHandler {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The data stored for offline discussions and posts changed its format. Adapt the entries already stored.
|
||||
* Since it can be slow, we'll only block migrating the db of current site, the rest will be in background.
|
||||
*
|
||||
* @return {Promise<any>} Promise resolved when the db is migrated.
|
||||
*/
|
||||
protected adaptForumOfflineStores(): Promise<any> {
|
||||
// @todo: Implement it once Forum addon is implemented.
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,22 +44,6 @@ export class CoreDomUtilsProvider {
|
|||
private platform: Platform, private configProvider: CoreConfigProvider, private urlUtils: CoreUrlUtilsProvider,
|
||||
private modalCtrl: ModalController) { }
|
||||
|
||||
/**
|
||||
* Wraps a message with core-format-text if the message contains HTML tags.
|
||||
* @todo Finish the adaptation
|
||||
*
|
||||
* @param {string} message Message to wrap.
|
||||
* @return {string} Result message.
|
||||
*/
|
||||
private addFormatTextIfNeeded(message: string): string {
|
||||
// @todo
|
||||
if (this.textUtils.hasHTMLTags(message)) {
|
||||
return '<core-format-text watch="true">' + message + '</core-format-text>';
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to element.closest(). If the browser doesn't support element.closest, it will
|
||||
* traverse the parents to achieve the same functionality.
|
||||
|
@ -776,24 +760,43 @@ export class CoreDomUtilsProvider {
|
|||
* @param {string} message Message to show.
|
||||
* @param {string} [buttonText] Text of the button.
|
||||
* @param {number} [autocloseTime] Number of milliseconds to wait to close the modal. If not defined, modal won't be closed.
|
||||
* @return {Alert} The alert modal.
|
||||
* @return {Promise<Alert>} Promise resolved with the alert modal.
|
||||
*/
|
||||
showAlert(title: string, message: string, buttonText?: string, autocloseTime?: number): Alert {
|
||||
const alert = this.alertCtrl.create({
|
||||
title: title,
|
||||
message: this.addFormatTextIfNeeded(message), // Add format-text to handle links.
|
||||
buttons: [buttonText || this.translate.instant('core.ok')]
|
||||
});
|
||||
showAlert(title: string, message: string, buttonText?: string, autocloseTime?: number): Promise<Alert> {
|
||||
const hasHTMLTags = this.textUtils.hasHTMLTags(message);
|
||||
let promise;
|
||||
|
||||
alert.present();
|
||||
|
||||
if (autocloseTime > 0) {
|
||||
setTimeout(() => {
|
||||
alert.dismiss();
|
||||
}, autocloseTime);
|
||||
if (hasHTMLTags) {
|
||||
// Format the text.
|
||||
promise = this.textUtils.formatText(message);
|
||||
} else {
|
||||
promise = Promise.resolve(message);
|
||||
}
|
||||
|
||||
return alert;
|
||||
return promise.then((message) => {
|
||||
|
||||
const alert = this.alertCtrl.create({
|
||||
title: title,
|
||||
message: message,
|
||||
buttons: [buttonText || this.translate.instant('core.ok')]
|
||||
});
|
||||
|
||||
alert.present().then(() => {
|
||||
if (hasHTMLTags) {
|
||||
// Treat all anchors so they don't override the app.
|
||||
const alertMessageEl: HTMLElement = alert.pageRef().nativeElement.querySelector('.alert-message');
|
||||
this.treatAnchors(alertMessageEl);
|
||||
}
|
||||
});
|
||||
|
||||
if (autocloseTime > 0) {
|
||||
setTimeout(() => {
|
||||
alert.dismiss();
|
||||
}, autocloseTime);
|
||||
}
|
||||
|
||||
return alert;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -803,9 +806,9 @@ export class CoreDomUtilsProvider {
|
|||
* @param {string} message Message to show.
|
||||
* @param {string} [buttonText] Text of the button.
|
||||
* @param {number} [autocloseTime] Number of milliseconds to wait to close the modal. If not defined, modal won't be closed.
|
||||
* @return {Alert} The alert modal.
|
||||
* @return {Promise<Alert>} Promise resolved with the alert modal.
|
||||
*/
|
||||
showAlertTranslated(title: string, message: string, buttonText?: string, autocloseTime?: number): Alert {
|
||||
showAlertTranslated(title: string, message: string, buttonText?: string, autocloseTime?: number): Promise<Alert> {
|
||||
title = title ? this.translate.instant(title) : title;
|
||||
message = message ? this.translate.instant(message) : message;
|
||||
buttonText = buttonText ? this.translate.instant(buttonText) : buttonText;
|
||||
|
@ -825,30 +828,50 @@ export class CoreDomUtilsProvider {
|
|||
*/
|
||||
showConfirm(message: string, title?: string, okText?: string, cancelText?: string, options?: any): Promise<void> {
|
||||
return new Promise<void>((resolve, reject): void => {
|
||||
options = options || {};
|
||||
const hasHTMLTags = this.textUtils.hasHTMLTags(message);
|
||||
let promise;
|
||||
|
||||
options.message = this.addFormatTextIfNeeded(message); // Add format-text to handle links.
|
||||
options.title = title;
|
||||
if (!title) {
|
||||
options.cssClass = 'core-nohead';
|
||||
if (hasHTMLTags) {
|
||||
// Format the text.
|
||||
promise = this.textUtils.formatText(message);
|
||||
} else {
|
||||
promise = Promise.resolve(message);
|
||||
}
|
||||
options.buttons = [
|
||||
{
|
||||
text: cancelText || this.translate.instant('core.cancel'),
|
||||
role: 'cancel',
|
||||
handler: (): void => {
|
||||
reject(this.createCanceledError());
|
||||
}
|
||||
},
|
||||
{
|
||||
text: okText || this.translate.instant('core.ok'),
|
||||
handler: (): void => {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this.alertCtrl.create(options).present();
|
||||
promise.then((message) => {
|
||||
options = options || {};
|
||||
|
||||
options.message = message;
|
||||
options.title = title;
|
||||
if (!title) {
|
||||
options.cssClass = 'core-nohead';
|
||||
}
|
||||
options.buttons = [
|
||||
{
|
||||
text: cancelText || this.translate.instant('core.cancel'),
|
||||
role: 'cancel',
|
||||
handler: (): void => {
|
||||
reject(this.createCanceledError());
|
||||
}
|
||||
},
|
||||
{
|
||||
text: okText || this.translate.instant('core.ok'),
|
||||
handler: (): void => {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const alert = this.alertCtrl.create(options);
|
||||
|
||||
alert.present().then(() => {
|
||||
if (hasHTMLTags) {
|
||||
// Treat all anchors so they don't override the app.
|
||||
const alertMessageEl: HTMLElement = alert.pageRef().nativeElement.querySelector('.alert-message');
|
||||
this.treatAnchors(alertMessageEl);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -858,9 +881,9 @@ export class CoreDomUtilsProvider {
|
|||
* @param {any} error Message to show.
|
||||
* @param {boolean} [needsTranslate] Whether the error needs to be translated.
|
||||
* @param {number} [autocloseTime] Number of milliseconds to wait to close the modal. If not defined, modal won't be closed.
|
||||
* @return {Alert} The alert modal.
|
||||
* @return {Promise<Alert>} Promise resolved with the alert modal.
|
||||
*/
|
||||
showErrorModal(error: any, needsTranslate?: boolean, autocloseTime?: number): Alert {
|
||||
showErrorModal(error: any, needsTranslate?: boolean, autocloseTime?: number): Promise<Alert> {
|
||||
if (typeof error == 'object') {
|
||||
// We received an object instead of a string. Search for common properties.
|
||||
if (error.coreCanceled) {
|
||||
|
@ -903,9 +926,9 @@ export class CoreDomUtilsProvider {
|
|||
* @param {any} [defaultError] Message to show if the error is not a string.
|
||||
* @param {boolean} [needsTranslate] Whether the error needs to be translated.
|
||||
* @param {number} [autocloseTime] Number of milliseconds to wait to close the modal. If not defined, modal won't be closed.
|
||||
* @return {Alert} The alert modal.
|
||||
* @return {Promise<Alert>} Promise resolved with the alert modal.
|
||||
*/
|
||||
showErrorModalDefault(error: any, defaultError: any, needsTranslate?: boolean, autocloseTime?: number): Alert {
|
||||
showErrorModalDefault(error: any, defaultError: any, needsTranslate?: boolean, autocloseTime?: number): Promise<Alert> {
|
||||
if (error && error.coreCanceled) {
|
||||
// It's a canceled error, don't display an error.
|
||||
return;
|
||||
|
@ -927,9 +950,9 @@ export class CoreDomUtilsProvider {
|
|||
* @param {any} [defaultError] Message to show if the error is not a string.
|
||||
* @param {boolean} [needsTranslate] Whether the error needs to be translated.
|
||||
* @param {number} [autocloseTime] Number of milliseconds to wait to close the modal. If not defined, modal won't be closed.
|
||||
* @return {Alert} The alert modal.
|
||||
* @return {Promise<Alert>} Promise resolved with the alert modal.
|
||||
*/
|
||||
showErrorModalFirstWarning(warnings: any, defaultError: any, needsTranslate?: boolean, autocloseTime?: number): Alert {
|
||||
showErrorModalFirstWarning(warnings: any, defaultError: any, needsTranslate?: boolean, autocloseTime?: number): Promise<Alert> {
|
||||
const error = warnings && warnings.length && warnings[0].message;
|
||||
|
||||
return this.showErrorModalDefault(error, defaultError, needsTranslate, autocloseTime);
|
||||
|
@ -974,32 +997,52 @@ export class CoreDomUtilsProvider {
|
|||
*/
|
||||
showPrompt(message: string, title?: string, placeholder?: string, type: string = 'password'): Promise<any> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
this.alertCtrl.create({
|
||||
message: this.addFormatTextIfNeeded(message), // Add format-text to handle links.
|
||||
title: title,
|
||||
inputs: [
|
||||
{
|
||||
name: 'promptinput',
|
||||
placeholder: placeholder || this.translate.instant('core.login.password'),
|
||||
type: type
|
||||
}
|
||||
],
|
||||
buttons: [
|
||||
{
|
||||
text: this.translate.instant('core.cancel'),
|
||||
role: 'cancel',
|
||||
handler: (): void => {
|
||||
reject();
|
||||
const hasHTMLTags = this.textUtils.hasHTMLTags(message);
|
||||
let promise;
|
||||
|
||||
if (hasHTMLTags) {
|
||||
// Format the text.
|
||||
promise = this.textUtils.formatText(message);
|
||||
} else {
|
||||
promise = Promise.resolve(message);
|
||||
}
|
||||
|
||||
promise.then((message) => {
|
||||
const alert = this.alertCtrl.create({
|
||||
message: message,
|
||||
title: title,
|
||||
inputs: [
|
||||
{
|
||||
name: 'promptinput',
|
||||
placeholder: placeholder || this.translate.instant('core.login.password'),
|
||||
type: type
|
||||
}
|
||||
},
|
||||
{
|
||||
text: this.translate.instant('core.ok'),
|
||||
handler: (data): void => {
|
||||
resolve(data.promptinput);
|
||||
],
|
||||
buttons: [
|
||||
{
|
||||
text: this.translate.instant('core.cancel'),
|
||||
role: 'cancel',
|
||||
handler: (): void => {
|
||||
reject();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: this.translate.instant('core.ok'),
|
||||
handler: (data): void => {
|
||||
resolve(data.promptinput);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
alert.present().then(() => {
|
||||
if (hasHTMLTags) {
|
||||
// Treat all anchors so they don't override the app.
|
||||
const alertMessageEl: HTMLElement = alert.pageRef().nativeElement.querySelector('.alert-message');
|
||||
this.treatAnchors(alertMessageEl);
|
||||
}
|
||||
]
|
||||
}).present();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1069,6 +1112,42 @@ export class CoreDomUtilsProvider {
|
|||
return element.children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Treat anchors inside alert/modals.
|
||||
*
|
||||
* @param {HTMLElement} container The HTMLElement that can contain anchors.
|
||||
*/
|
||||
protected treatAnchors(container: HTMLElement): void {
|
||||
const anchors = Array.from(container.querySelectorAll('a'));
|
||||
|
||||
anchors.forEach((anchor) => {
|
||||
anchor.addEventListener('click', (event) => {
|
||||
if (event.defaultPrevented) {
|
||||
// Stop.
|
||||
return;
|
||||
}
|
||||
|
||||
const href = anchor.getAttribute('href');
|
||||
if (href) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// We cannot use CoreDomUtilsProvider.openInBrowser due to circular dependencies.
|
||||
if (this.appProvider.isDesktop()) {
|
||||
// It's a desktop app, use Electron shell library to open the browser.
|
||||
const shell = require('electron').shell;
|
||||
if (!shell.openExternal(href)) {
|
||||
// Open browser failed, open a new window in the app.
|
||||
window.open(href, '_system');
|
||||
}
|
||||
} else {
|
||||
window.open(href, '_system');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* View an image in a new page or modal.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue