From e5978b7eeb617ce2963f5ce9625b7d5e38ead267 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 30 Nov 2017 12:02:49 +0100 Subject: [PATCH] MOBILE-2253 login: Implement reconnect page --- src/app/app.scss | 34 ++++ src/core/login/pages/reconnect/reconnect.html | 54 ++++++ .../login/pages/reconnect/reconnect.module.ts | 35 ++++ src/core/login/pages/reconnect/reconnect.scss | 47 +++++ src/core/login/pages/reconnect/reconnect.ts | 168 ++++++++++++++++++ 5 files changed, 338 insertions(+) create mode 100644 src/core/login/pages/reconnect/reconnect.html create mode 100644 src/core/login/pages/reconnect/reconnect.module.ts create mode 100644 src/core/login/pages/reconnect/reconnect.scss create mode 100644 src/core/login/pages/reconnect/reconnect.ts diff --git a/src/app/app.scss b/src/app/app.scss index 81f002012..a1a043c65 100644 --- a/src/app/app.scss +++ b/src/app/app.scss @@ -124,3 +124,37 @@ ion-icon.icon-accessory { // Set bgcolor in content instead of overriding $background-color because that variable is applied to a lot of places. background-color: $gray-light; } + +// Large centered avatar. +.item-avatar-center { + text-align: center; + padding-left: 16px; + + &.item-complex .item-content { + text-align: center; + padding-left: 49px; + } + + > img:first-child, + ion-avatar img { + display: block; + margin: auto; + width: 90px; + height: 90px; + max-width: 90px; + max-height: 90px; + margin-bottom: 10px; + border-radius : 50%; + padding: 4px; + border: 1px solid #ddd; + + &.avatar-full { + border-radius: 2%; + border: 0; + max-width: 100%; + max-height: 160px; + width: auto; + height: auto; + } + } +} diff --git a/src/core/login/pages/reconnect/reconnect.html b/src/core/login/pages/reconnect/reconnect.html new file mode 100644 index 000000000..c6f800266 --- /dev/null +++ b/src/core/login/pages/reconnect/reconnect.html @@ -0,0 +1,54 @@ + + + {{ 'mm.login.reconnect' | translate }} + + + + + + + + {{ 'mm.core.pictureof' | translate:{$a: site.fullname} }} + + + + + + + +

{{siteUrl}}

+ +

{{siteName}}

+

{{siteUrl}}

+ +

+ {{ 'mm.login.reconnectdescription' | translate }} +

+
+ +

{{ 'mm.login.username' | translate }}

+

{{username}}

+
+
+ + + + + + + {{ 'mm.login.cancel' | translate }} + + +
+ + +
+

{{ 'mm.login.potentialidps' | translate }}

+ + {{provider.name}} + {{provider.name}} + + +
+
+
diff --git a/src/core/login/pages/reconnect/reconnect.module.ts b/src/core/login/pages/reconnect/reconnect.module.ts new file mode 100644 index 000000000..929973d46 --- /dev/null +++ b/src/core/login/pages/reconnect/reconnect.module.ts @@ -0,0 +1,35 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { IonicPageModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreLoginReconnectPage } from './reconnect'; +import { CoreLoginModule } from '../../login.module'; +import { CoreComponentsModule } from '../../../../components/components.module'; +import { CoreDirectivesModule } from '../../../../directives/directives.module'; + +@NgModule({ + declarations: [ + CoreLoginReconnectPage + ], + imports: [ + CoreComponentsModule, + CoreDirectivesModule, + CoreLoginModule, + IonicPageModule.forChild(CoreLoginReconnectPage), + TranslateModule.forChild() + ] +}) +export class CoreLoginReconnectPageModule {} diff --git a/src/core/login/pages/reconnect/reconnect.scss b/src/core/login/pages/reconnect/reconnect.scss new file mode 100644 index 000000000..aa4c48163 --- /dev/null +++ b/src/core/login/pages/reconnect/reconnect.scss @@ -0,0 +1,47 @@ +page-core-login-reconnect { + .content { + .mm-ioninput-password { + padding-top: 0; + padding-bottom: 0; + } + background: -webkit-radial-gradient(white, $gray-light); + background: radial-gradient(white, $gray-light); + + img { + max-width: 100%; + } + + img.moodle-logo { + width: 90%; + max-width: 300px; + } + + .box { + padding: 16px; + background: $white; + border: 1px solid $gray; + } + + .mm-sitename, .mm-siteurl { + @if $mm-fixed-url { display: none; } + } + + .list .item-input, .list .mm-username { + border: 1px solid $list-border-color; + margin-bottom: 20px; + + .label { + margin: 0; + } + } + + .list .mm-username { + &.item-md { + @include padding-horizontal($item-md-padding-left * 1.5, $content-padding); + } + &.item-ios { + @include padding-horizontal($item-ios-padding-left * 1.5, $content-padding); + } + } + } +} diff --git a/src/core/login/pages/reconnect/reconnect.ts b/src/core/login/pages/reconnect/reconnect.ts new file mode 100644 index 000000000..162bb62d5 --- /dev/null +++ b/src/core/login/pages/reconnect/reconnect.ts @@ -0,0 +1,168 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 } from '@angular/core'; +import { IonicPage, NavController, NavParams } from 'ionic-angular'; +import { CoreAppProvider } from '../../../../providers/app'; +import { CoreSitesProvider } from '../../../../providers/sites'; +import { CoreDomUtilsProvider } from '../../../../providers/utils/dom'; +import { CoreLoginHelperProvider } from '../../providers/helper'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; + +/** + * Page to enter the user password to reconnect to a site. + */ +@IonicPage() +@Component({ + selector: 'page-core-login-reconnect', + templateUrl: 'reconnect.html', +}) +export class CoreLoginReconnectPage { + credForm: FormGroup; + siteUrl: string; + username: string; + siteName: string; + logoUrl: string; + identityProviders: any[]; + site: any; + + protected infoSiteUrl: string; + protected pageName: string; + protected pageParams: any; + protected siteConfig: any; + protected isLoggedOut: boolean; + protected siteId: string; + + constructor(private navCtrl: NavController, navParams: NavParams, fb: FormBuilder, private appProvider: CoreAppProvider, + private sitesProvider: CoreSitesProvider, private loginHelper: CoreLoginHelperProvider, + private domUtils: CoreDomUtilsProvider) { + + let currentSite = this.sitesProvider.getCurrentSite(); + + this.infoSiteUrl = navParams.get('infoSiteUrl'); + this.pageName = navParams.get('pageName'); + this.pageParams = navParams.get('pageParams'); + this.siteConfig = navParams.get('siteConfig'); + this.siteUrl = navParams.get('siteUrl'); + this.siteId = navParams.get('siteId'); + + this.isLoggedOut = currentSite && currentSite.isLoggedOut(); + this.credForm = fb.group({ + 'password': ['', Validators.required] + }); + } + + /** + * View loaded. + */ + ionViewDidLoad() { + if (this.siteConfig) { + this.identityProviders = this.loginHelper.getValidIdentityProviders(this.siteConfig); + } + + this.sitesProvider.getSite(this.siteId).then((site) => { + this.site = { + id: site.id, + fullname: site.infos.fullname, + avatar: site.infos.userpictureurl + }; + + this.username = site.infos.username; + this.siteUrl = site.infos.siteurl; + this.siteName = site.infos.sitename; + + // Check logoURL if user avatar is not set. + if (this.site.avatar.startsWith(site.infos.siteurl + '/theme/image.php')) { + this.site.avatar = false; + return site.getPublicConfig().then((config) => { + this.logoUrl = config.logourl || config.compactlogourl; + }); + } + }).catch(() => { + // Shouldn't happen. Just leave the view. + this.cancel(); + }); + + } + + /** + * Cancel reconnect. + */ + cancel() { + this.sitesProvider.logout().finally(() => { + this.navCtrl.setRoot('CoreLoginSitesPage'); + }); + } + + /** + * Tries to authenticate the user. + */ + login() : void { + this.appProvider.closeKeyboard(); + + // Get input data. + let siteUrl = this.siteUrl, + username = this.username, + password = this.credForm.value.password; + + if (!password) { + this.domUtils.showErrorModal('mm.login.passwordrequired', true); + return; + } + + if (!this.appProvider.isOnline()) { + this.domUtils.showErrorModal('mm.core.networkerrormsg', true); + return; + } + + let modal = this.domUtils.showModalLoading(); + + // Start the authentication process. + this.sitesProvider.getUserToken(siteUrl, username, password).then((data) => { + return this.sitesProvider.updateSiteToken(this.infoSiteUrl, username, data.token, data.privateToken).then(() => { + // Update site info too because functions might have changed (e.g. unisntall local_mobile). + return this.sitesProvider.updateSiteInfoByUrl(this.infoSiteUrl, username).finally(() => { + // Reset fields so the data is not in the view anymore. + this.credForm.controls['password'].reset(); + + if (this.pageName) { + // Page defined, go to that page instead of site initial page. + return this.navCtrl.setRoot(this.pageName, this.pageParams); + } else { + return this.loginHelper.goToSiteInitialPage(this.navCtrl, true); + } + }).catch((error) => { + // Site deleted? Go back to login page. + this.domUtils.showErrorModal('mm.login.errorupdatesite', true); + this.cancel(); + }); + }); + }).catch((error) => { + this.loginHelper.treatUserTokenError(siteUrl, error); + }).finally(() => { + modal.dismiss(); + }); + } + + /** + * An OAuth button was clicked. + * + * @param {any} provider The provider that was clicked. + */ + oauthClicked(provider) : void { + if (!this.loginHelper.openBrowserForOAuthLogin(this.siteUrl, provider, this.siteConfig.launchurl)) { + this.domUtils.showErrorModal('Invalid data.'); + } + } +}