commit
f519d56ea7
|
@ -6454,7 +6454,7 @@
|
||||||
"integrity": "sha512-EYC5eQFVkoYXq39l7tYKE6lEjHJ04mvTmKXxGL7quHLdFPfJMNzru/UYpn92AOfpl3PQaZmou78C7EgmFOwFQQ=="
|
"integrity": "sha512-EYC5eQFVkoYXq39l7tYKE6lEjHJ04mvTmKXxGL7quHLdFPfJMNzru/UYpn92AOfpl3PQaZmou78C7EgmFOwFQQ=="
|
||||||
},
|
},
|
||||||
"cordova-plugin-wkuserscript": {
|
"cordova-plugin-wkuserscript": {
|
||||||
"version": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git#6413f4bb3c2565f353e690b5c1450b69ad9e860e",
|
"version": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git#aa77d0f98a3fb106f2e798e5adf5882f01a2c947",
|
||||||
"from": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git"
|
"from": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git"
|
||||||
},
|
},
|
||||||
"cordova-plugin-wkwebview-cookies": {
|
"cordova-plugin-wkwebview-cookies": {
|
||||||
|
|
|
@ -16,7 +16,8 @@ import { Component, OnInit } from '@angular/core';
|
||||||
import { NavController } from '@ionic/angular';
|
import { NavController } from '@ionic/angular';
|
||||||
|
|
||||||
import { CoreLangProvider } from '@services/lang';
|
import { CoreLangProvider } from '@services/lang';
|
||||||
import { CoreEvents } from '@singletons/events';
|
import { CoreLoginHelperProvider } from '@core/login/services/helper';
|
||||||
|
import { CoreEvents, CoreEventSessionExpiredData } from '@singletons/events';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
|
@ -28,6 +29,7 @@ export class AppComponent implements OnInit {
|
||||||
constructor(
|
constructor(
|
||||||
protected langProvider: CoreLangProvider,
|
protected langProvider: CoreLangProvider,
|
||||||
protected navCtrl: NavController,
|
protected navCtrl: NavController,
|
||||||
|
protected loginHelper: CoreLoginHelperProvider,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +48,11 @@ export class AppComponent implements OnInit {
|
||||||
// @todo
|
// @todo
|
||||||
// this.removeVersionClass();
|
// this.removeVersionClass();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Listen for session expired events.
|
||||||
|
CoreEvents.on(CoreEvents.SESSION_EXPIRED, (data: CoreEventSessionExpiredData) => {
|
||||||
|
this.loginHelper.sessionExpired(data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
<ng-container *ngIf="control && control.dirty && !control.valid">
|
<ng-container *ngIf="control && control.dirty && !control.valid">
|
||||||
<ng-container *ngFor="let error of errorKeys">
|
<ng-container *ngFor="let error of errorKeys">
|
||||||
<div *ngIf="control.hasError(error)" class="core-input-error">
|
<div *ngIf="control.hasError(error)" class="core-input-error">
|
||||||
<span *ngIf="errorMessages[error]">{{errorMessages[error]}}</span>
|
<span *ngIf="errorMessages && errorMessages[error]">{{errorMessages[error]}}</span>
|
||||||
<span *ngIf="!errorMessages[error] && error == 'max' && control.errors.max">
|
<span *ngIf="(!errorMessages || !errorMessages[error]) && error == 'max' && control.errors?.max">
|
||||||
{{ 'core.login.invalidvaluemax' | translate:{$a: control.errors.max.max} }}
|
{{ 'core.login.invalidvaluemax' | translate:{$a: control.errors!.max.max} }}
|
||||||
</span>
|
</span>
|
||||||
<span *ngIf="!errorMessages[error] && error == 'min' && control.errors.min">
|
<span *ngIf="(!errorMessages || !errorMessages[error]) && error == 'min' && control.errors?.min">
|
||||||
{{ 'core.login.invalidvaluemin' | translate:{$a: control.errors.min.min} }}
|
{{ 'core.login.invalidvaluemin' | translate:{$a: control.errors!.min.min} }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
|
@ -39,21 +39,25 @@ const routes: Routes = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'forgottenpassword',
|
path: 'forgottenpassword',
|
||||||
loadChildren: () => import('./pages/forgotten-password/forgotten-password.module')
|
loadChildren: () => import('./pages/forgotten-password/forgotten-password.page.module')
|
||||||
.then( m => m.CoreLoginForgottenPasswordPageModule),
|
.then( m => m.CoreLoginForgottenPasswordPageModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'changepassword',
|
path: 'changepassword',
|
||||||
loadChildren: () => import('./pages/change-password/change-password.module')
|
loadChildren: () => import('./pages/change-password/change-password.page.module')
|
||||||
.then( m => m.CoreLoginChangePasswordPageModule),
|
.then( m => m.CoreLoginChangePasswordPageModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'sitepolicy',
|
path: 'sitepolicy',
|
||||||
loadChildren: () => import('./pages/site-policy/site-policy.module').then( m => m.CoreLoginSitePolicyPageModule),
|
loadChildren: () => import('./pages/site-policy/site-policy.page.module').then( m => m.CoreLoginSitePolicyPageModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'emailsignup',
|
path: 'emailsignup',
|
||||||
loadChildren: () => import('./pages/email-signup/email-signup.module').then( m => m.CoreLoginEmailSignupPageModule),
|
loadChildren: () => import('./pages/email-signup/email-signup.page.module').then( m => m.CoreLoginEmailSignupPageModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'reconnect',
|
||||||
|
loadChildren: () => import('./pages/reconnect/reconnect.page.module').then( m => m.CoreLoginReconnectPageModule),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.core-sitename {
|
.core-sitename {
|
||||||
font-size: 1.8rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.core-login-site-logo {
|
.core-login-site-logo {
|
||||||
|
|
|
@ -28,7 +28,7 @@ import { CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes
|
||||||
import { CoreEvents } from '@singletons/events';
|
import { CoreEvents } from '@singletons/events';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays a "splash screen" while the app is being initialized.
|
* Page to enter the user credentials.
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'page-core-login-credentials',
|
selector: 'page-core-login-credentials',
|
||||||
|
@ -161,7 +161,7 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy {
|
||||||
*/
|
*/
|
||||||
protected treatSiteConfig(): void {
|
protected treatSiteConfig(): void {
|
||||||
if (this.siteConfig) {
|
if (this.siteConfig) {
|
||||||
this.siteName = CoreConstants.CONFIG.sitename ?? this.siteConfig.sitename;
|
this.siteName = CoreConstants.CONFIG.sitename ? CoreConstants.CONFIG.sitename : this.siteConfig.sitename;
|
||||||
this.logoUrl = CoreLoginHelper.instance.getLogoUrl(this.siteConfig);
|
this.logoUrl = CoreLoginHelper.instance.getLogoUrl(this.siteConfig);
|
||||||
this.authInstructions = this.siteConfig.authinstructions || Translate.instance.instant('core.login.loginsteps');
|
this.authInstructions = this.siteConfig.authinstructions || Translate.instance.instant('core.login.loginsteps');
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@
|
||||||
<ion-input type="text" name="nameField" placeholder="{{ 'core.user.' + nameField | translate }}"
|
<ion-input type="text" name="nameField" placeholder="{{ 'core.user.' + nameField | translate }}"
|
||||||
formControlName="{{nameField}}" autocorrect="off">
|
formControlName="{{nameField}}" autocorrect="off">
|
||||||
</ion-input>
|
</ion-input>
|
||||||
<core-input-errors [control]="signupForm.controls[nameField]" [errorMessages]="namefieldsErrors[nameField]">
|
<core-input-errors [control]="signupForm.controls[nameField]" [errorMessages]="namefieldsErrors![nameField]">
|
||||||
</core-input-errors>
|
</core-input-errors>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item class="ion-text-wrap">
|
<ion-item class="ion-text-wrap">
|
||||||
|
|
|
@ -337,7 +337,7 @@ export class CoreLoginEmailSignupPage implements OnInit {
|
||||||
/**
|
/**
|
||||||
* Show authentication instructions.
|
* Show authentication instructions.
|
||||||
*/
|
*/
|
||||||
protected showAuthInstructions(): void {
|
showAuthInstructions(): void {
|
||||||
CoreTextUtils.instance.viewText(Translate.instance.instant('core.login.instructions'), this.authInstructions!);
|
CoreTextUtils.instance.viewText(Translate.instance.instant('core.login.instructions'), this.authInstructions!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<ion-back-button></ion-back-button>
|
||||||
|
</ion-buttons>
|
||||||
|
|
||||||
|
<ion-title>{{ 'core.login.reconnect' | translate }}</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<div class="ion-text-wrap ion-text-center ion-margin-bottom" [ngClass]="{'item-avatar-center': showSiteAvatar}">
|
||||||
|
<ng-container *ngIf="showSiteAvatar">
|
||||||
|
<ion-avatar>
|
||||||
|
<!-- Show user avatar. -->
|
||||||
|
<img [src]="userAvatar" class="avatar" core-external-content [siteId]="siteId" role="presentation"
|
||||||
|
alt="{{ 'core.pictureof' | translate:{$a: userFullName} }}" onError="this.src='assets/img/user-avatar.png'">
|
||||||
|
</ion-avatar>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<div class="core-login-site-logo" *ngIf="!showSiteAvatar">
|
||||||
|
<!-- Show site logo or a default image. -->
|
||||||
|
<img *ngIf="logoUrl" [src]="logoUrl" role="presentation" onError="this.src='assets/img/login_logo.png'">
|
||||||
|
<img *ngIf="!logoUrl" src="assets/img/login_logo.png" role="presentation">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h6 *ngIf="siteName" class="ion-padding core-sitename">
|
||||||
|
<core-format-text [text]="siteName" [filter]="false"></core-format-text>
|
||||||
|
</h6>
|
||||||
|
<p class="core-siteurl">{{siteUrl}}</p>
|
||||||
|
|
||||||
|
<ion-item *ngIf="!isLoggedOut" class="ion-text-center core-login-reconnect-warning" lines="none">
|
||||||
|
<ion-label color="danger">
|
||||||
|
<ion-icon name="fas-exclamation-circle" slot="start"></ion-icon>
|
||||||
|
{{ 'core.login.reconnectdescription' | translate }}
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</div>
|
||||||
|
<form ion-list *ngIf="!isOAuth" [formGroup]="credForm" (ngSubmit)="login($event)" class="core-login-form" #reconnectForm>
|
||||||
|
<ion-item class="ion-text-wrap core-username">
|
||||||
|
<ion-label>
|
||||||
|
<p>{{username}}</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-margin-bottom">
|
||||||
|
<core-show-password [name]="'password'">
|
||||||
|
<ion-input class="core-ioninput-password" name="password" type="password"
|
||||||
|
placeholder="{{ 'core.login.password' | translate }}" formControlName="password" [clearOnEdit]="false">
|
||||||
|
</ion-input>
|
||||||
|
</core-show-password>
|
||||||
|
</ion-item>
|
||||||
|
<ion-grid class="ion-padding">
|
||||||
|
<ion-row>
|
||||||
|
<ion-col>
|
||||||
|
<ion-button expand="block" color="light" (click)="cancel($event)">{{ 'core.login.cancel' | translate }}</ion-button>
|
||||||
|
</ion-col>
|
||||||
|
<ion-col>
|
||||||
|
<ion-button type="submit" expand="block" [disabled]="!credForm.valid">{{ 'core.login.loginbutton' | translate }}</ion-button>
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
</ion-grid>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Forgotten password option. -->
|
||||||
|
<ion-list lines="none" *ngIf="showForgottenPassword && !isOAuth" class="core-login-forgotten-password ion-no-padding">
|
||||||
|
<ion-item button class="ion-text-center ion-text-wrap" (click)="forgottenPassword()" detail="false">
|
||||||
|
<ion-label>
|
||||||
|
{{ 'core.login.forgotten' | translate }}
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-list>
|
||||||
|
|
||||||
|
<!-- Identity providers. -->
|
||||||
|
<ion-list *ngIf="identityProviders && identityProviders.length" class="ion-padding-top core-login-identity-providers">
|
||||||
|
<ion-item class="ion-text-wrap" lines="none">
|
||||||
|
<ion-label><h3 class="item-heading">{{ 'core.login.potentialidps' | translate }}</h3></ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item button *ngFor="let provider of identityProviders" class="ion-text-wrap core-oauth-icon"
|
||||||
|
(click)="oauthClicked(provider)" title="{{provider.name}}">
|
||||||
|
<img [src]="provider.iconurl" alt="" width="32" height="32" slot="start">
|
||||||
|
<ion-label>{{provider.name}}</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-list>
|
||||||
|
|
||||||
|
<!-- If OAuth, display cancel button since the form isn't displayed. -->
|
||||||
|
<ion-list *ngIf="isOAuth">
|
||||||
|
<ion-button expand="block" class="ion-margin" color="light" (click)="cancel($event)">
|
||||||
|
{{ 'core.login.cancel' | translate }}
|
||||||
|
</ion-button>
|
||||||
|
</ion-list>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,50 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { IonicModule } from '@ionic/angular';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
|
||||||
|
import { CoreLoginReconnectPage } from './reconnect.page';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: CoreLoginReconnectPage,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild(routes),
|
||||||
|
CommonModule,
|
||||||
|
IonicModule,
|
||||||
|
TranslateModule.forChild(),
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
CoreLoginReconnectPage,
|
||||||
|
],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class CoreLoginReconnectPageModule {}
|
|
@ -0,0 +1,245 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
|
||||||
|
import { ActivatedRoute, Params } from '@angular/router';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { NavController } from '@ionic/angular';
|
||||||
|
|
||||||
|
import { CoreApp } from '@services/app';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { CoreLoginHelper } from '@core/login/services/helper';
|
||||||
|
import { CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes/site';
|
||||||
|
import { CoreEvents } from '@singletons/events';
|
||||||
|
import { CoreError } from '@classes/errors/error';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page to enter the user password to reconnect to a site.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'page-core-login-reconnect',
|
||||||
|
templateUrl: 'reconnect.html',
|
||||||
|
styleUrls: ['../../login.scss'],
|
||||||
|
})
|
||||||
|
export class CoreLoginReconnectPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
@ViewChild('reconnectForm') formElement?: ElementRef;
|
||||||
|
|
||||||
|
credForm: FormGroup;
|
||||||
|
siteUrl!: string;
|
||||||
|
username!: string;
|
||||||
|
userFullName!: string;
|
||||||
|
userAvatar?: string;
|
||||||
|
siteName!: string;
|
||||||
|
logoUrl?: string;
|
||||||
|
identityProviders?: CoreSiteIdentityProvider[];
|
||||||
|
showForgottenPassword = true;
|
||||||
|
showSiteAvatar = false;
|
||||||
|
isOAuth = false;
|
||||||
|
isLoggedOut: boolean;
|
||||||
|
siteId!: string;
|
||||||
|
|
||||||
|
protected page?: string;
|
||||||
|
protected pageParams?: Params;
|
||||||
|
protected siteConfig?: CoreSitePublicConfigResponse;
|
||||||
|
protected viewLeft = false;
|
||||||
|
protected eventThrown = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected navCtrl: NavController,
|
||||||
|
protected fb: FormBuilder,
|
||||||
|
protected route: ActivatedRoute,
|
||||||
|
) {
|
||||||
|
|
||||||
|
const currentSite = CoreSites.instance.getCurrentSite();
|
||||||
|
|
||||||
|
this.isLoggedOut = !!currentSite?.isLoggedOut();
|
||||||
|
this.credForm = fb.group({
|
||||||
|
password: ['', Validators.required],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the component.
|
||||||
|
*/
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
const params = this.route.snapshot.queryParams;
|
||||||
|
|
||||||
|
this.siteId = params['siteId'];
|
||||||
|
this.page = params['pageName'];
|
||||||
|
this.pageParams = params['pageParams'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const site = await CoreSites.instance.getSite(this.siteId);
|
||||||
|
|
||||||
|
if (!site.infos) {
|
||||||
|
throw new CoreError('Invalid site');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.username = site.infos.username;
|
||||||
|
this.userFullName = site.infos.fullname;
|
||||||
|
this.userAvatar = site.infos.userpictureurl;
|
||||||
|
this.siteUrl = site.infos.siteurl;
|
||||||
|
this.siteName = site.getSiteName();
|
||||||
|
|
||||||
|
// If login was OAuth we should only reach this page if the OAuth method ID has changed.
|
||||||
|
this.isOAuth = site.isOAuth();
|
||||||
|
|
||||||
|
// Show logo instead of avatar if it's a fixed site.
|
||||||
|
this.showSiteAvatar = !!this.userAvatar && !CoreLoginHelper.instance.getFixedSites();
|
||||||
|
|
||||||
|
const config = await CoreUtils.instance.ignoreErrors(site.getPublicConfig());
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.siteConfig = config;
|
||||||
|
|
||||||
|
await CoreSites.instance.checkRequiredMinimumVersion(config);
|
||||||
|
|
||||||
|
// Check logoURL if user avatar is not set.
|
||||||
|
if (this.userAvatar.startsWith(this.siteUrl + '/theme/image.php')) {
|
||||||
|
this.showSiteAvatar = false;
|
||||||
|
}
|
||||||
|
this.logoUrl = CoreLoginHelper.instance.getLogoUrl(config);
|
||||||
|
|
||||||
|
this.getDataFromConfig(this.siteConfig);
|
||||||
|
} catch (error) {
|
||||||
|
// Just leave the view.
|
||||||
|
this.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component destroyed.
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.viewLeft = true;
|
||||||
|
CoreEvents.trigger(CoreEvents.LOGIN_SITE_UNCHECKED, { config: this.siteConfig }, this.siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get some data (like identity providers) from the site config.
|
||||||
|
*
|
||||||
|
* @param config Config to use.
|
||||||
|
*/
|
||||||
|
protected getDataFromConfig(config: CoreSitePublicConfigResponse): void {
|
||||||
|
const disabledFeatures = CoreLoginHelper.instance.getDisabledFeatures(config);
|
||||||
|
|
||||||
|
this.identityProviders = CoreLoginHelper.instance.getValidIdentityProviders(config, disabledFeatures);
|
||||||
|
this.showForgottenPassword = !CoreLoginHelper.instance.isForgottenPasswordDisabled(config);
|
||||||
|
|
||||||
|
if (!this.eventThrown && !this.viewLeft) {
|
||||||
|
this.eventThrown = true;
|
||||||
|
CoreEvents.trigger(CoreEvents.LOGIN_SITE_CHECKED, { config: config });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel reconnect.
|
||||||
|
*
|
||||||
|
* @param e Event.
|
||||||
|
*/
|
||||||
|
cancel(e?: Event): void {
|
||||||
|
if (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreSites.instance.logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to authenticate the user.
|
||||||
|
*
|
||||||
|
* @param e Event.
|
||||||
|
*/
|
||||||
|
async login(e: Event): Promise<void> {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
CoreApp.instance.closeKeyboard();
|
||||||
|
|
||||||
|
// Get input data.
|
||||||
|
const password = this.credForm.value.password;
|
||||||
|
|
||||||
|
if (!password) {
|
||||||
|
CoreDomUtils.instance.showErrorModal('core.login.passwordrequired', true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CoreApp.instance.isOnline()) {
|
||||||
|
CoreDomUtils.instance.showErrorModal('core.networkerrormsg', true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modal = await CoreDomUtils.instance.showModalLoading();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Start the authentication process.
|
||||||
|
const data = await CoreSites.instance.getUserToken(this.siteUrl, this.username, password);
|
||||||
|
|
||||||
|
await CoreSites.instance.updateSiteToken(this.siteUrl, this.username, data.token, data.privateToken);
|
||||||
|
|
||||||
|
CoreDomUtils.instance.triggerFormSubmittedEvent(this.formElement, true);
|
||||||
|
|
||||||
|
// Update site info too.
|
||||||
|
await CoreSites.instance.updateSiteInfoByUrl(this.siteUrl, this.username);
|
||||||
|
|
||||||
|
// Reset fields so the data is not in the view anymore.
|
||||||
|
this.credForm.controls['password'].reset();
|
||||||
|
|
||||||
|
// Go to the site initial page.
|
||||||
|
await CoreLoginHelper.instance.goToSiteInitialPage({
|
||||||
|
redirectPage: this.page,
|
||||||
|
redirectParams: this.pageParams,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
CoreLoginHelper.instance.treatUserTokenError(this.siteUrl, error, this.username, password);
|
||||||
|
|
||||||
|
if (error.loggedout) {
|
||||||
|
this.cancel();
|
||||||
|
} else if (error.errorcode == 'forcepasswordchangenotice') {
|
||||||
|
// Reset password field.
|
||||||
|
this.credForm.controls.password.reset();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
modal.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forgotten password button clicked.
|
||||||
|
*/
|
||||||
|
forgottenPassword(): void {
|
||||||
|
CoreLoginHelper.instance.forgottenPasswordClicked(this.siteUrl, this.username, this.siteConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An OAuth button was clicked.
|
||||||
|
*
|
||||||
|
* @param provider The provider that was clicked.
|
||||||
|
*/
|
||||||
|
oauthClicked(provider: CoreSiteIdentityProvider): void {
|
||||||
|
if (!CoreLoginHelper.instance.openBrowserForOAuthLogin(this.siteUrl, provider, this.siteConfig?.launchurl)) {
|
||||||
|
CoreDomUtils.instance.showErrorModal('Invalid data.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ import { makeSingleton, Translate } from '@singletons/core.singletons';
|
||||||
import { CoreLogger } from '@singletons/logger';
|
import { CoreLogger } from '@singletons/logger';
|
||||||
import { CoreUrl } from '@singletons/url';
|
import { CoreUrl } from '@singletons/url';
|
||||||
import { NavigationOptions } from '@ionic/angular/providers/nav-controller';
|
import { NavigationOptions } from '@ionic/angular/providers/nav-controller';
|
||||||
|
import { CoreObject } from '@/app/singletons/object';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper provider that provides some common features regarding authentication.
|
* Helper provider that provides some common features regarding authentication.
|
||||||
|
@ -126,7 +127,7 @@ export class CoreLoginHelperProvider {
|
||||||
const currentSite = CoreSites.instance.getCurrentSite();
|
const currentSite = CoreSites.instance.getCurrentSite();
|
||||||
const currentPage = CoreApp.instance.getCurrentPage();
|
const currentPage = CoreApp.instance.getCurrentPage();
|
||||||
|
|
||||||
if (!CoreApp.instance.isSSOAuthenticationOngoing() && currentSite?.isLoggedOut() && currentPage == 'login/reconnect') {
|
if (!CoreApp.instance.isSSOAuthenticationOngoing() && currentSite?.isLoggedOut() && currentPage == '/login/reconnect') {
|
||||||
// User must reauthenticate but he closed the InAppBrowser without doing so, logout him.
|
// User must reauthenticate but he closed the InAppBrowser without doing so, logout him.
|
||||||
CoreSites.instance.logout();
|
CoreSites.instance.logout();
|
||||||
}
|
}
|
||||||
|
@ -1106,14 +1107,11 @@ export class CoreLoginHelperProvider {
|
||||||
this.isOpeningReconnect = true;
|
this.isOpeningReconnect = true;
|
||||||
|
|
||||||
await CoreUtils.instance.ignoreErrors(this.navCtrl.navigateRoot('/login/reconnect', {
|
await CoreUtils.instance.ignoreErrors(this.navCtrl.navigateRoot('/login/reconnect', {
|
||||||
queryParams: {
|
queryParams: CoreObject.removeUndefined({
|
||||||
infoSiteUrl: info.siteurl,
|
siteId,
|
||||||
siteUrl: result.siteUrl,
|
|
||||||
siteId: siteId,
|
|
||||||
pageName: data.pageName,
|
pageName: data.pageName,
|
||||||
pageParams: data.params,
|
pageParams: data.params,
|
||||||
siteConfig: result.config,
|
}),
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.isOpeningReconnect = false;
|
this.isOpeningReconnect = false;
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { CoreComponentsModule } from '@components/components.module';
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
|
|
||||||
import { CoreSettingsDeviceInfoPage } from './deviceinfo.page';
|
import { CoreSettingsDeviceInfoPage } from './deviceinfo.page';
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ const routes: Routes = [
|
||||||
TranslateModule.forChild(),
|
TranslateModule.forChild(),
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule,
|
CoreDirectivesModule,
|
||||||
|
CorePipesModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
CoreSettingsDeviceInfoPage,
|
CoreSettingsDeviceInfoPage,
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { NavController } from '@ionic/angular';
|
import { NavController } from '@ionic/angular';
|
||||||
import { WKUserScriptWindow, WKUserScriptInjectionTime } from 'cordova-plugin-wkuserscript';
|
import { WKUserScriptWindow } from 'cordova-plugin-wkuserscript';
|
||||||
import { WKWebViewCookiesWindow } from 'cordova-plugin-wkwebview-cookies';
|
import { WKWebViewCookiesWindow } from 'cordova-plugin-wkwebview-cookies';
|
||||||
|
|
||||||
import { CoreApp } from '@services/app';
|
import { CoreApp } from '@services/app';
|
||||||
|
@ -470,7 +470,7 @@ export class CoreIframeUtilsProvider {
|
||||||
userScriptWindow.WKUserScript?.addScript({
|
userScriptWindow.WKUserScript?.addScript({
|
||||||
id: 'CoreIframeUtilsRecaptchaScript',
|
id: 'CoreIframeUtilsRecaptchaScript',
|
||||||
file: recaptchaPath,
|
file: recaptchaPath,
|
||||||
injectionTime: WKUserScriptInjectionTime.END,
|
injectionTime: userScriptWindow.WKUserScript?.InjectionTime.END,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle post messages received by iframes.
|
// Handle post messages received by iframes.
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton with helper functions for objects.
|
||||||
|
*/
|
||||||
|
export class CoreObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all keys from an object whose value are null or undefined.
|
||||||
|
*
|
||||||
|
* @param object Object to modify.
|
||||||
|
*/
|
||||||
|
static removeUndefined<T>(object: T): T {
|
||||||
|
for (const name in object) {
|
||||||
|
if (object[name] === undefined) {
|
||||||
|
delete object[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue