Skip to content

Commit

Permalink
Merge pull request moodlehq#4274 from crazyserver/MOBILE-4680
Browse files Browse the repository at this point in the history
Mobile 4680
  • Loading branch information
dpalou authored Jan 8, 2025
2 parents 6af9840 + 6c132bd commit 988a5b6
Show file tree
Hide file tree
Showing 15 changed files with 231 additions and 99 deletions.
1 change: 1 addition & 0 deletions scripts/langindex.json
Original file line number Diff line number Diff line change
Expand Up @@ -2168,6 +2168,7 @@
"core.login.missingfirstname": "moodle",
"core.login.missinglastname": "moodle",
"core.login.mobileservicesnotenabled": "local_moodlemobileapp",
"core.login.morewaystologin": "local_moodlemobileapp",
"core.login.mustconfirm": "moodle",
"core.login.newaccount": "moodle",
"core.login.notloggedin": "local_moodlemobileapp",
Expand Down
27 changes: 10 additions & 17 deletions src/core/classes/sites/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ export class CoreSite extends CoreAuthenticatedSite {
* Check if the user authenticated in the site using an OAuth method.
*
* @returns Whether the user authenticated in the site using an OAuth method.
* @deprecated since 5.0. Use getOAuthId instead.
*/
isOAuth(): boolean {
return this.oauthId != null && this.oauthId !== undefined;
Expand Down Expand Up @@ -268,7 +269,6 @@ export class CoreSite extends CoreAuthenticatedSite {
*
* @param component Component name.
* @param componentId Component id.
* @returns Promise resolved when the entries are deleted.
*/
async deleteComponentFromCache(component: string, componentId?: number): Promise<void> {
if (!component) {
Expand All @@ -284,7 +284,7 @@ export class CoreSite extends CoreAuthenticatedSite {
await this.cacheTable.delete(params);
}

/*
/**
* Uploads a file using Cordova File API.
*
* @param filePath File path.
Expand Down Expand Up @@ -366,13 +366,17 @@ export class CoreSite extends CoreAuthenticatedSite {
* @param url The url to be fixed.
* @returns Promise resolved with the fixed URL.
*/
checkAndFixPluginfileURL(url: string): Promise<string> {
return this.checkTokenPluginFile(url).then(() => this.fixPluginfileURL(url));
async checkAndFixPluginfileURL(url: string): Promise<string> {
// Resolve the checking promise to make sure it's finished.
await this.checkTokenPluginFile(url);

// The previous promise (tokenPluginFileWorks) result will be used here.
return this.fixPluginfileURL(url);
}

/**
* Generic function for adding the wstoken to Moodle urls and for pointing to the correct script.
* Uses CoreUtilsProvider.fixPluginfileURL, passing site's token.
* Uses CoreUrl.fixPluginfileURL, passing site's token.
*
* @param url The url to be fixed.
* @returns Fixed URL.
Expand All @@ -386,17 +390,13 @@ export class CoreSite extends CoreAuthenticatedSite {

/**
* Deletes site's DB.
*
* @returns Promise to be resolved when the DB is deleted.
*/
async deleteDB(): Promise<void> {
await CoreDB.deleteDB('Site-' + this.id);
}

/**
* Deletes site's folder.
*
* @returns Promise to be resolved when the DB is deleted.
*/
async deleteFolder(): Promise<void> {
if (!CoreFile.isAvailable() || !this.id) {
Expand Down Expand Up @@ -466,7 +466,6 @@ export class CoreSite extends CoreAuthenticatedSite {
* @param url The URL to open.
* @param alertMessage If defined, an alert will be shown before opening the browser.
* @param options Other options.
* @returns Promise resolved when done, rejected otherwise.
*/
async openInBrowserWithAutoLogin(
url: string,
Expand Down Expand Up @@ -598,8 +597,6 @@ export class CoreSite extends CoreAuthenticatedSite {

/**
* Invalidates config WS call.
*
* @returns Promise resolved when the data is invalidated.
*/
async invalidateConfig(): Promise<void> {
await this.invalidateWsCacheForKey(this.getConfigCacheKey());
Expand Down Expand Up @@ -728,7 +725,6 @@ export class CoreSite extends CoreAuthenticatedSite {
* Deletes a site setting.
*
* @param name The config name.
* @returns Promise resolved when done.
*/
async deleteSiteConfig(name: string): Promise<void> {
await this.configTable.deleteByPrimaryKey({ name });
Expand Down Expand Up @@ -760,13 +756,12 @@ export class CoreSite extends CoreAuthenticatedSite {
*
* @param name The config name.
* @param value The config value. Can only store number or strings.
* @returns Promise resolved when done.
*/
async setLocalSiteConfig(name: string, value: number | string): Promise<void> {
await this.configTable.insert({ name, value });
}

/*
/**
* Check if tokenpluginfile script works in the site.
*
* @param url URL to check.
Expand Down Expand Up @@ -802,7 +797,6 @@ export class CoreSite extends CoreAuthenticatedSite {
* Deletes last viewed records based on some conditions.
*
* @param conditions Conditions.
* @returns Promise resolved when done.
*/
async deleteLastViewed(conditions?: Partial<CoreSiteLastViewedDBRecord>): Promise<void> {
await this.lastViewedTable.delete(conditions);
Expand Down Expand Up @@ -853,7 +847,6 @@ export class CoreSite extends CoreAuthenticatedSite {
* @param id ID.
* @param value Last viewed item value.
* @param options Options.
* @returns Promise resolved when done.
*/
async storeLastViewed(
component: string,
Expand Down
2 changes: 2 additions & 0 deletions src/core/features/login/components/components.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { NgModule } from '@angular/core';
import { CoreSharedModule } from '@/core/shared.module';
import { CoreLoginMethodsComponent } from './login-methods/login-methods';
import { CoreLoginExceededAttemptsComponent } from '@features/login/components/exceeded-attempts/exceeded-attempts';
import { CoreLoginIdentityProviderComponent } from './identity-provider/identity-provider';

@NgModule({
declarations: [
Expand All @@ -24,6 +25,7 @@ import { CoreLoginExceededAttemptsComponent } from '@features/login/components/e
],
imports: [
CoreSharedModule,
CoreLoginIdentityProviderComponent,
],
exports: [
CoreLoginExceededAttemptsComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<ion-button class="ion-text-wrap ion-margin core-oauth-provider" (click)="openOAuth()" [ariaLabel]="provider.name" expand="block"
fill="outline">
@if (provider.iconurl) {
<img [src]="provider.iconurl" alt="" width="32" height="32" slot="start" aria-hidden="true" (error)="provider.iconurl = ''" />
}
<ion-label>{{ provider.name }}</ion-label>
</ion-button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// (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, Input } from '@angular/core';
import { CoreSiteIdentityProvider } from '@classes/sites/unauthenticated-site';
import { CoreLoginHelper } from '@features/login/services/login-helper';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreSharedModule } from '@/core/shared.module';
import { CoreRedirectPayload } from '@services/navigator';

@Component({
selector: 'core-identity-provider',
templateUrl: 'identity-provider.html',
standalone: true,
imports: [
CoreSharedModule,
],
})
export class CoreLoginIdentityProviderComponent {

@Input({ required: true }) provider!: CoreSiteIdentityProvider;
@Input() launchurl = '';
@Input() siteUrl = '';
@Input() redirectData?: CoreRedirectPayload;

/**
* The button has been clicked.
*/
async openOAuth(): Promise<void> {
const result = await CoreLoginHelper.openBrowserForOAuthLogin(
this.siteUrl,
this.provider,
this.launchurl,
this.redirectData,
);

if (!result) {
CoreDomUtils.showErrorModal('Invalid data.');
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@
<!-- Identity providers. -->
<ion-list *ngIf="identityProviders.length" class="core-login-identity-providers">
<h2 class="item-heading">{{ 'core.login.potentialidps' | translate }}</h2>
<ion-button [fill]="'outline'" *ngFor="let provider of identityProviders" class="ion-text-wrap ion-margin core-oauth-provider"
(click)="oauthClicked(provider)" [ariaLabel]="provider.name" expand="block">
<img *ngIf="provider.iconurl" [src]="provider.iconurl" alt="" width="32" height="32" slot="start" aria-hidden="true">
<ion-label>{{ provider.name }}</ion-label>
</ion-button>
<core-identity-provider *ngFor="let provider of identityProviders" [provider]="provider" [launchurl]="siteConfig?.launchurl"
[redirectData]="redirectData" [siteUrl]="siteUrl" />
</ion-list>
67 changes: 43 additions & 24 deletions src/core/features/login/components/login-methods/login-methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { toBoolean } from '@/core/transforms/boolean';
import { Component, Input, OnInit } from '@angular/core';
import { CorePromisedValue } from '@classes/promised-value';
import { CoreSite } from '@classes/sites/site';
import { CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes/sites/unauthenticated-site';
import { CoreLoginHelper, CoreLoginMethod } from '@features/login/services/login-helper';
import { CoreRedirectPayload } from '@services/navigator';
import { CoreSites } from '@services/sites';
import { CoreSitesFactory } from '@services/sites-factory';
import { CoreDomUtils } from '@services/utils/dom';

@Component({
selector: 'core-login-methods',
Expand All @@ -28,27 +27,31 @@ import { CoreDomUtils } from '@services/utils/dom';
})
export class CoreLoginMethodsComponent implements OnInit {

@Input({ transform: toBoolean }) reconnect = false;
@Input() siteUrl = '';
@Input() siteConfig?: CoreSitePublicConfigResponse;
@Input() redirectData?: CoreRedirectPayload;
@Input() site?: CoreSite; // Defined when the user is reconnecting.
@Input() showLoginForm = true;

isBrowserSSO = false;
showScanQR = false;
loginMethods: CoreLoginMethod[] = [];
identityProviders: CoreSiteIdentityProvider[] = [];

protected currentLoginProvider?: CoreSiteIdentityProvider;
protected isReady = new CorePromisedValue<void>();

/**
* @inheritdoc
*/
async ngOnInit(): Promise<void> {
if (this.reconnect) {
if (this.site) {
this.siteUrl = this.site.getURL();

this.loginMethods = await CoreLoginHelper.getLoginMethods();

const currentSite = CoreSites.getCurrentSite();
const defaultMethod = await CoreLoginHelper.getDefaultLoginMethod();
if (currentSite?.isLoggedOut() && defaultMethod) {
if (this.site.isLoggedOut() && defaultMethod) {
await defaultMethod.action();
}
}
Expand All @@ -59,25 +62,29 @@ export class CoreLoginMethodsComponent implements OnInit {
// Identity providers won't be shown if login on browser.
if (!this.isBrowserSSO) {
this.identityProviders = await CoreLoginHelper.getValidIdentityProvidersForSite(
CoreSitesFactory.makeUnauthenticatedSite(this.siteUrl, this.siteConfig),
this.site ?? CoreSitesFactory.makeUnauthenticatedSite(this.siteUrl, this.siteConfig),
);
}

if (this.reconnect) {
if (this.site) {
this.showScanQR = CoreLoginHelper.displayQRInSiteScreen();

// The identity provider set in the site will be shown at the top.
const oAuthId = this.site.getOAuthId();
this.currentLoginProvider = CoreLoginHelper.findIdentityProvider(this.identityProviders, oAuthId);
}

// If still false or credentials screen.
if (!this.reconnect || !this.showScanQR) {
if (!this.site || !this.showScanQR) {
this.showScanQR = await CoreLoginHelper.displayQRInCredentialsScreen(this.siteConfig.tool_mobile_qrcodetype);
}
}

this.isReady.resolve();
}

/**
* Show instructions and scan QR code.
*
* @returns Promise resolved when done.
*/
async showInstructionsAndScanQR(): Promise<void> {
try {
Expand All @@ -90,21 +97,33 @@ export class CoreLoginMethodsComponent implements OnInit {
}

/**
* An OAuth button was clicked.
* Get the current login, removing the identity provider from the list.
*
* @param provider The provider that was clicked.
* @returns Current login.
*/
async oauthClicked(provider: CoreSiteIdentityProvider): Promise<void> {
const result = await CoreLoginHelper.openBrowserForOAuthLogin(
this.siteUrl,
provider,
this.siteConfig?.launchurl,
this.redirectData,
);

if (!result) {
CoreDomUtils.showErrorModal('Invalid data.');
async extractCurrentLogin(): Promise<CoreLoginMethodsCurrentLogin | undefined> {
await this.isReady;

if (!this.currentLoginProvider) {
return;
}

// Remove the identity provider from the array.
this.identityProviders = this.identityProviders.filter((provider) =>
provider.url !== this.currentLoginProvider?.url);

const showOther = !!(this.showLoginForm || this.isBrowserSSO) &&
!!(this.loginMethods.length || this.identityProviders.length || this.showScanQR);

return {
provider: this.currentLoginProvider,
showOther,
};
}

}

export type CoreLoginMethodsCurrentLogin = {
provider: CoreSiteIdentityProvider;
showOther: boolean;
};
1 change: 1 addition & 0 deletions src/core/features/login/lang.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"missingfirstname": "Missing given name",
"missinglastname": "Missing last name",
"mobileservicesnotenabled": "Mobile services are not enabled on the site.",
"morewaystologin": "More ways to log in",
"mustconfirm": "You need to confirm your account",
"newaccount": "New account",
"notloggedin": "You need to be logged in.",
Expand Down
2 changes: 2 additions & 0 deletions src/core/features/login/login-reconnect-lazy.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { CoreSharedModule } from '@/core/shared.module';
import { CoreLoginComponentsModule } from '@features/login/components/components.module';
import { CoreLoginReconnectPage } from '@features/login/pages/reconnect/reconnect';
import { CoreSiteLogoComponent } from '@/core/components/site-logo/site-logo';
import { CoreLoginIdentityProviderComponent } from './components/identity-provider/identity-provider';

const routes: Routes = [
{
Expand All @@ -33,6 +34,7 @@ const routes: Routes = [
CoreSharedModule,
CoreLoginComponentsModule,
CoreSiteLogoComponent,
CoreLoginIdentityProviderComponent,
],
declarations: [
CoreLoginReconnectPage,
Expand Down
Loading

0 comments on commit 988a5b6

Please sign in to comment.